Jekyll2023-07-31T10:29:05+05:30https://dollardhingra.com/feed.xmlDollar Dhingra’s BlogSoftware Developer's blogBenchmarking Python JSON serializers - json vs ujson vs orjson2022-05-25T00:00:00+05:302022-05-25T22:40:00+05:30https://dollardhingra.com/blog/python-json-benchmarking<h2 id="introduction">Introduction</h2>
<p>If you work with a large datasets in json inside your python code,
then you might want to try using 3rd party libraries like <a href="https://github.com/ultrajson/ultrajson">ujson</a>
and <a href="https://github.com/ijl/orjson">orjson</a>
which are replacements to python’s json library.</p>
<p>As per their documentation</p>
<ul>
<li>
<p><a href="https://github.com/ultrajson/ultrajson">ujson</a> (UltraJSON) is an ultra fast JSON encoder and decoder written in pure C with bindings for Python 3.7+.</p>
</li>
<li>
<p><a href="https://github.com/ijl/orjson">orjson</a> is a fast, correct JSON library for Python. It is the fastest python library for json encoding & decoding. It serializes dataclass, datetime, numpy, and UUID instances natively.</p>
</li>
</ul>
<h2 id="benchmarking">Benchmarking</h2>
<p>I did a basic benchmark comparing json, ujson and orjson.
The benchmarking results were interesting.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">orjson</span>
<span class="kn">import</span> <span class="nn">ujson</span>
<span class="k">def</span> <span class="nf">benchmark</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">dumps</span><span class="p">,</span> <span class="n">loads</span><span class="p">):</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3000000</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">dumps</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="n">loads</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">m</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"timestamp"</span><span class="p">:</span> <span class="mf">1556283673.1523004</span><span class="p">,</span>
<span class="s">"task_uuid"</span><span class="p">:</span> <span class="s">"0ed1a1c3-050c-4fb9-9426-a7e72d0acfc7"</span><span class="p">,</span>
<span class="s">"task_level"</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span>
<span class="s">"action_status"</span><span class="p">:</span> <span class="s">"started"</span><span class="p">,</span>
<span class="s">"action_type"</span><span class="p">:</span> <span class="s">"main"</span><span class="p">,</span>
<span class="s">"key"</span><span class="p">:</span> <span class="s">"value"</span><span class="p">,</span>
<span class="s">"another_key"</span><span class="p">:</span> <span class="mi">123</span><span class="p">,</span>
<span class="s">"and_another"</span><span class="p">:</span> <span class="p">[</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">],</span>
<span class="p">}</span>
<span class="n">benchmark</span><span class="p">(</span><span class="s">"Python"</span><span class="p">,</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">,</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">)</span>
<span class="n">benchmark</span><span class="p">(</span><span class="s">"ujson"</span><span class="p">,</span> <span class="n">ujson</span><span class="p">.</span><span class="n">dumps</span><span class="p">,</span> <span class="n">ujson</span><span class="p">.</span><span class="n">loads</span><span class="p">)</span>
<span class="c1"># orjson only outputs bytes, but often we need unicode:
</span> <span class="n">benchmark</span><span class="p">(</span><span class="s">"orjson"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">orjson</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="s">"utf-8"</span><span class="p">),</span> <span class="n">orjson</span><span class="p">.</span><span class="n">loads</span><span class="p">)</span>
<span class="c1"># OUTPUT:
# Python 12.502133846282959
# ujson 4.428200960159302
# orjson 2.3136467933654785
</span></code></pre></div></div>
<blockquote>
<p><a href="https://github.com/ultrajson/ultrajson">ujson</a> is 3 times faster than the standard json library</p>
</blockquote>
<blockquote>
<p><a href="https://github.com/ijl/orjson">orjson</a> is over 6 times faster than the standard json library</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>For most cases, you would want to go with python’s standard json library which removes
dependencies on other libraries. On other hand you could try out <a href="https://github.com/ultrajson/ultrajson">ujson</a>
which is simple replacement for python’s json library. If you want more speed and also want
dataclass, datetime, numpy, and UUID instances and you are ready to deal with more complex code,
then you can try your hands on <a href="https://github.com/ijl/orjson">orjson</a></p>If you work with a large datasets in json inside your python code, then you might want to try using 3rd party libraries like ujson and orjsonBus Factor in Software Engineering2022-01-22T00:00:00+05:302022-01-25T22:00:00+05:30https://dollardhingra.com/blog/bus-lottery-factor-software-engineering<h1 id="1-introduction">1. Introduction</h1>
<p>Imagine that John Doe works on a project along with 7-10 folks. John started
working on this project from the beginning and therefore knows a lot more than everyone else in the project.</p>
<p>These leads to some problems such as:</p>
<ol>
<li>John is the go-to person for each detail that the team needs. John has a lot of responsibility and workload on his shoulders, making his progress sometimes slow.</li>
<li>Whenever John falls sick and goes on unplanned leave, he is bugged by his team to keep things moving forward in the project.</li>
<li>The organisation finds it hard to replace John and has therefore retained him twice in the past year by raising his compensation twice.</li>
</ol>
<blockquote>
<p>In short, the project has a single point of failure -> John Doe</p>
</blockquote>
<h1 id="2-what-is-bus-factor">2. What is Bus Factor?</h1>
<p>As per Wikipedia, The bus factor is a measurement of the risk resulting from information and capabilities not
being shared among team members, derived from the phrase “in case they get hit by a bus”.
It is also known as the bread truck scenario, bus problem, beer truck scenario, lottery factor, truck factor,
bus/truck number, or lorry factor.</p>
<p>In simple words, what is the probability of the project failing if John Doe gets hit by a bus and is not able to return to work?</p>
<blockquote>
<p>Higher the bus factor higher the risk of failure.</p>
</blockquote>
<h1 id="3-reducingpreventing-the-bus-factor-in-software-development">3. Reducing/Preventing the Bus Factor in Software Development</h1>
<h2 id="31-documenting">3.1 Documenting</h2>
<p>Creating and maintaining documentation can take a good amount of effort from all
the stakeholders. But in the long run, it is worth documenting. Documenting includes Product Requirement
Documents(PRDs), High & Low-level design documents, changelogs, READMEs, complete ticket descriptions on ticketing tools,
Root Cause Analysis documents in case of production fires, etc</p>
<h2 id="32-code-reviews">3.2 Code Reviews</h2>
<p>If done effectively, code reviews can act as an excellent tool to reduce the bus
factor, given that everyone in the team gets a chance to review the code.
The Engineering manager/team lead should make sure that there are relative code
review guidelines set up as per the team so that it is effective enough.</p>
<h2 id="33-meetings">3.3 Meetings</h2>
<p>Yes, I know meetings sometimes slow us down and hinder our productivity. Also, some meetings can
just be replaced by an email. But, meetings (especially if you are working remotely) can actively reduce the
bus factor if done correctly. Meetings like standup, sprint planning/grooming, design discussions,
sprint retros etc bring everyone together as a team. Imagine you reading a minute of meeting document vs
actually brainstorming in the meeting. <strong>Meetings do not replace the documentation. Instead, the
documentation complements the meeting process.</strong></p>
<h2 id="34-team-process">3.4 Team Process</h2>
<p>More team processes can improve the knowledge distribution in the team like:</p>
<ul>
<li>Pair programming</li>
<li>Office hours</li>
<li>On-call rotations</li>
<li>Engineering demos</li>
<li>Knowledge sharing sessions</li>
</ul>
<h2 id="35-positive-work-culture">3.5 Positive Work culture</h2>
<p>A positive culture means employees are happy in the organisation and a better work environment is created.
Following techniques can be applied to improve the work culture and reduce the bus factor:</p>
<h3 id="351-mandatory-paid-annual-vacation">3.5.1 Mandatory Paid Annual Vacation</h3>
<p>Most of the organisations provide paid leave to their employees. The organisations should also encourage
people to use these leaves and go on a vacation(for a week at least). This will enable other employees to take
care of work in the absence of an employee.</p>
<h3 id="352-short-noticetermination-period">3.5.2 Short Notice/Termination Period</h3>
<p>A short notice period means, the organisation making sure that there is less dependency on an employee.</p>
<h1 id="conclusion">Conclusion</h1>
<p>The first step is to talk about Bus factor in your team, even if you are a junior member in
the team. Maybe, some of the above methods like meetings, code reviews, documenting etc.
are already being used in your organisation for sharing knowledge. You can consider optimising those methods to reduce/prevent
the bus factor in your team. For eg, you can consider including the junior members of your
team in meetings and code reviews. The members with a centralized knowledge can work on
spreading their knowledge in the team so that they can spend their time and energy on
something new for the team.</p>
<p>What do you think of Bus factor in your team? Do you have more methods to prevent high bus
factor? Feel free to give your feedback!</p>What happens to your project, if the most important member of your team doesn't return to work the next day?How to install older Ruby versions(2.x) on Mac M1 without using arch x86_642022-01-20T00:00:00+05:302022-01-20T21:00:00+05:30https://dollardhingra.com/blog/install-ruby-old-versions-macm1<h1 id="introduction">Introduction</h1>
<p>After spending hours of searching different solutions on the internet for <strong>installing older version of ruby (2.5.x,
2.6.x etc ) on Mac M1</strong>, I finally figured out the solution and decided to document it by writing a blog.</p>
<h1 id="pre-requirements">Pre-Requirements</h1>
<p>Make sure you have following installed on your Mac M1</p>
<ul>
<li>Update your Mac OS</li>
<li>Homebrew
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
</code></pre></div> </div>
</li>
<li>rbenv (Should work for rvm also)
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install rbenv
</code></pre></div> </div>
</li>
<li>Make sure that you have latest xcode command line tools
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select --install
</code></pre></div> </div>
<p>if the above fails, then:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
</code></pre></div> </div>
</li>
</ul>
<h1 id="steps-to-reproduce-the-problem">Steps to Reproduce The Problem</h1>
<p>The problem occurs if you try to install an older version with Ruby.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rbenv install 2.6.2
</code></pre></div></div>
<p>Error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- BUILD FAILED (macOS 12.1 using ruby-build 20210707)
- Inspect or clean up the working tree at `/var/folders/.....`
</code></pre></div></div>
<h1 id="solution">Solution</h1>
<p>Try installing the ruby version with this variable <code class="language-plaintext highlighter-rouge">RUBY_CFLAGS="-Wno-error=implicit-function-declaration"</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RUBY_CFLAGS="-Wno-error=implicit-function-declaration" rbenv install 2.6.2
</code></pre></div></div>
<p>This should work fine!</p>
<h1 id="other-installations">Other Installations</h1>
<p>After this, install other dependencies normally using brew. For eg: postgresql
I installed postgresql@10 which was required for the project.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install postgresql@10
</code></pre></div></div>
<p>The end of success clearly states that you have to do some entries in your bash/zsh file</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>If you need to have postgresql@10 first in your PATH, run:
echo 'export PATH="/opt/homebrew/opt/postgresql@10/bin:$PATH"' >> ~/.zshrc
For compilers to find postgresql@10 you may need to set:
export LDFLAGS="-L/opt/homebrew/opt/postgresql@10/lib"
export CPPFLAGS="-I/opt/homebrew/opt/postgresql@10/include"
For pkg-config to find postgresql@10 you may need to set:
export PKG_CONFIG_PATH="/opt/homebrew/opt/postgresql@10/lib/pkgconfig"
To restart postgresql@10 after an upgrade:
brew services restart postgresql@10
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/postgresql@10/bin/postgres -D /opt/homebrew/var/postgresql@10
</code></pre></div></div>
<p>Do these changes and then go to your project and set the ruby version for the project:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rbenv local 2.6.2
</code></pre></div></div>
<p>Check if the ruby version is correct:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby -v # should output whatever version you had set in the previous step.
</code></pre></div></div>
<p>The next step is to install the bundle</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle install
</code></pre></div></div>
<p>During the installation, I got an error while installing http-parser. The error message looked
like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Caused by:
LoadError: cannot load such file -- 2.6/ffi_c
Caused by:
LoadError: cannot load such file -- ffi-compiler/compile_task
(See full trace by running task with --trace)
rake failed, exit code 1
Gem files will remain installed in /Users/Dollar/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/http-parser-1.2.3 for inspection.
Results logged to /Users/Dollar/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/extensions/-darwin-21/2.6.0/http-parser-1.2.3/gem_make.out
An error occurred while installing http-parser (1.2.3), and Bundler cannot continue.
Make sure that `gem install http-parser -v '1.2.3' --source 'https://rubygems.org/'` succeeds before bundling.
In Gemfile:
elastic-apm was resolved to 3.15.1, which depends on
http was resolved to 4.4.1, which depends on
http-parser
</code></pre></div></div>
<p>The error message clearly states that the reason</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LoadError: cannot load such file -- ffi-compiler/compile_task
</code></pre></div></div>
<p>I rectified the problem through installing ffi compiler for ruby platform using the following
command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install ffi --platform=ruby
</code></pre></div></div>
<p>Now, try running</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle install
</code></pre></div></div>
<h1 id="conclusion">Conclusion</h1>
<p>Things should work now! Feel free to drop a suggestion or any feedback if this process
can be further improved.</p>I spent hours of searching to figure out an easy way to install older versions of ruby on mac m12021 year in review2022-01-01T00:00:00+05:302022-01-01T21:00:00+05:30https://dollardhingra.com/blog/2021-year-review<p>Looking back at your achievements at the end of every year is a great way to boost your confidence and tackle the imposter syndrome :)
So, here we go!</p>
<ul>
<li><strong>1st prize at Remote Hackathon</strong> - Participated in the company’s
internal remote hackathon with 6 other team members.
Checkout more details <a href="https://www.linkedin.com/posts/tothenew_geekcombat-hackathon-activity-6775326843689025536-WWY1?utm_source=linkedin_share&utm_medium=member_desktop_web">here</a></li>
<li>
<p>Started writing technical blogs on topics related to python programming and
software engineering. I also self-hosted the blog <a href="https://dollardhingra.com">here</a> with
my first ever <a href="https://dollardhingra.com/blog/python-abstract-base-class/">article</a>. Since then
I try to write at least 1 blog article each month. **This year itself my posts had 20k views **.</p>
</li>
<li><strong>Delivered talks</strong> on following topics at my workplace
<ul>
<li>Introduction to ELasticsearch</li>
<li>Clean Code</li>
<li>Data Structures in Python</li>
</ul>
</li>
<li>Created an open source telegram bot for covid : <a href="https://dollardhingra.com/projects/#covid19-india-resources---telegram-bot">covid19-india-resources</a>,
<a href="https://github.com/dollardhingra/cowinquick">repo link</a></li>
<li>
<p>Created a <a href="https://dollardhingra.com/cowinquick/">panel</a> for checking vaccine booking slots. Booked 30+ slots for family and friends using this panel, <a href="https://github.com/dollardhingra/covid19indiaresources">Repo link</a></p>
</li>
<li>Read the following books:
<ul>
<li><a href="https://www.amazon.in/System-Design-Interview-insiders-Second/dp/B08CMF2CQF">System Design Interview</a></li>
<li><a href="https://www.amazon.in/Using-Asyncio-Python-Understanding-Asynchronous/dp/9352139674/ref=sr_1_1?crid=2OHIII2QZBKME&keywords=understanding+asyncio&qid=1647712919&s=books&sprefix=underdtanding+asyncio%2Cstripbooks%2C349&sr=1-1">Understanding Asyncio</a></li>
<li><a href="https://www.amazon.in/No-Drama-Discipline-Whole-Brain-Nurture-Developing/dp/0345548043/ref=sr_1_1?keywords=no+drama+discipline+book&qid=1647713084&s=books&sprefix=no+dra%2Cstripbooks%2C187&sr=1-1">No Drama Discipline</a></li>
</ul>
</li>
<li>Awesome movies that I watched:
<ul>
<li><a href="https://www.imdb.com/title/tt0268978/">A beautiful mind</a></li>
<li><a href="https://www.imdb.com/title/tt6751668/">Parasite</a></li>
</ul>
</li>
<li>Accepted a job offer from <a href="https://www.1mg.com/">Tata 1mg</a> as SDE-3. We are hiring at <a href="https://www.1mg.com/">Tata 1mg</a>. Checkout the <a href="https://1mg.darwinbox.in/ms/candidate/careers">jobs</a> and let me know if you are interested to work at India’s leading healthcare brand.</li>
</ul>What I achieved and what I missed in the year 2021Best Practices For Writing Clean Pythonic Code2021-12-05T00:00:00+05:302021-12-05T00:00:00+05:30https://dollardhingra.com/blog/python-code-best-practices<h2 id="introduction">Introduction</h2>
<p>This article is a collection of best practices for more idiomatic python code especially if you are new to python.</p>
<h2 id="contribute">Contribute</h2>
<p>Feel free to <a href="https://github.com/dollardhingra/dollardhingra.github.io/edit/master/_posts/2021-12-05-python-code-best-practices.md">contribute</a> to this list, and I will thank you by including a link to your profile with the snippet!</p>
<h2 id="1-catching-exceptions">1. Catching Exceptions</h2>
<p>This is a sure-shot way to get your code into trouble</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># very bad practice
</span><span class="k">try</span><span class="p">:</span>
<span class="n">do_something</span><span class="p">()</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="c1"># A slightly better way(included logging), but still a bad practice:
</span><span class="k">try</span><span class="p">:</span>
<span class="n">do_something</span><span class="p">()</span>
<span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
<span class="n">logging</span><span class="p">.</span><span class="n">exception</span><span class="p">()</span> <span class="c1"># although, logging is a good practice
</span>
<span class="c1"># When we don't use `Exception` we will also be catching events like system exit.
# So, using Exception means that we are only catching Exceptions.
</span>
<span class="c1"># A better way:
</span><span class="k">try</span><span class="p">:</span>
<span class="n">do_something</span><span class="p">()</span>
<span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
<span class="n">logging</span><span class="p">.</span><span class="n">exception</span><span class="p">()</span>
<span class="c1"># some code to handle your exception gracefully if required
</span>
</code></pre></div></div>
<p>Here we have used a specific type of Exception i.e. <code class="language-plaintext highlighter-rouge">ValueError</code>.</p>
<h2 id="2-name-casing">2. Name Casing</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1"># using camelCase is not a convention in python
</span><span class="k">def</span> <span class="nf">isEmpty</span><span class="p">(</span><span class="n">sampleArr</span><span class="p">):</span>
<span class="p">...</span>
<span class="c1"># instead use snake_case in python
</span><span class="k">def</span> <span class="nf">is_empty</span><span class="p">(</span><span class="n">sample_arr</span><span class="p">):</span>
<span class="p">...</span>
</code></pre></div></div>
<p>In python, <code class="language-plaintext highlighter-rouge">snake_case</code> is preferred for variables, functions and method names. However, for classes, <code class="language-plaintext highlighter-rouge">PascalCase</code> is used.</p>
<h2 id="3-chained-comparison-operators">3. Chained comparison operators</h2>
<p>There are multiple ways of comparing in Python:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Don't do this:
</span><span class="k">if</span> <span class="mi">0</span> <span class="o"><</span> <span class="n">x</span> <span class="ow">and</span> <span class="n">x</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'x is greater than 0 but less than 10'</span><span class="p">)</span>
<span class="c1"># Instead, do this:
</span><span class="k">if</span> <span class="mi">0</span> <span class="o"><</span> <span class="n">x</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'x is greater than 0 but less than 10'</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="4-mutable-default-arguments">4. Mutable default arguments</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1"># wrong way!
# a sure shot way to get some unintended bugs in your code
</span><span class="k">def</span> <span class="nf">add_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">,</span> <span class="n">box</span><span class="o">=</span><span class="p">[]):</span>
<span class="n">box</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">box</span>
<span class="c1"># correct way!
# recommended way for handling mutable default arguments:
</span><span class="k">def</span> <span class="nf">add_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">,</span> <span class="n">box</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">box</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">box</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">box</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">box</span>
</code></pre></div></div>
<p>Read more about mutable default arguments <a href="/blog/python-mutable-default-arguments/">here</a></p>
<h2 id="5-string-formatting">5. String Formatting</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1"># Avoid using it
# %-formatting
</span><span class="n">name</span> <span class="o">=</span> <span class="s">"James Bond"</span>
<span class="n">profession</span> <span class="o">=</span> <span class="s">"Secret Agent"</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Hello, %s. You are a %s."</span> <span class="o">%</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">profession</span><span class="p">))</span>
<span class="c1"># slightly better
# str.format()
</span><span class="k">print</span><span class="p">(</span><span class="s">"Hello, {}. You are a {}."</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">profession</span><span class="p">))</span>
<span class="c1"># Short, crisp and faster!
# f-strings
</span><span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Hello, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">. You are a </span><span class="si">{</span><span class="n">profession</span><span class="si">}</span><span class="s">."</span><span class="p">)</span>
</code></pre></div></div>
<p>The f in f-strings may as well stand for “fast.” f-strings are faster than both %-formatting and str.format(). <a href="https://www.python.org/dev/peps/pep-0498/#abstract">(Source)</a></p>
<h2 id="6-top-level-script-environment">6. Top-level script environment</h2>
<p>Executes only if it is run as a script and not as a module</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Filename: run.py
</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Hello from script!'</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ python run.py
$ Hello from script!
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Hello from script!</code> will <strong>not</strong> be printed if the module is imported into any other module.</p>
<h2 id="7-conditional-expressions">7. Conditional expressions</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">2</span>
</code></pre></div></div>
<p>Can be reduced to this one:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="mi">10</span> <span class="k">else</span> <span class="mi">2</span>
</code></pre></div></div>
<h2 id="8-iterating-over-an-iterator">8. Iterating over an iterator</h2>
<p>You don’t necessarily need to iterate over the indices of the elements in an iterator if you don’t need them. You can iterate directly over the elements.
This makes your code more pythonic.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">list_of_fruits</span> <span class="o">=</span> <span class="p">[</span><span class="s">"apple"</span><span class="p">,</span> <span class="s">"pear"</span><span class="p">,</span> <span class="s">"orange"</span><span class="p">]</span>
<span class="c1"># bad practice
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">list_of_fruits</span><span class="p">)):</span>
<span class="n">fruit</span> <span class="o">=</span> <span class="n">list_of_fruits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">process_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="c1"># good practice
</span><span class="k">for</span> <span class="n">fruit</span> <span class="ow">in</span> <span class="n">list_of_fruits</span><span class="p">:</span>
<span class="n">process_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="9-indexingcounting-during-iteration">9. Indexing/Counting during iteration</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Don't do this:
</span><span class="n">index</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">collection</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="n">index</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="c1"># Nor this:
</span><span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">collection</span><span class="p">)):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">collection</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="c1"># Definitely don't do this:
</span><span class="n">index</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">index</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">collection</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">collection</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="n">index</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="c1"># Instead, use `enumerate()`
</span><span class="k">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">collection</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="10-using-context-managers">10. Using context managers</h2>
<p>Python provides context managers that manage the overhead of initializing and clearing up the resources and let
you focus on the implementation. For example in the case of reading a file, you don’t need to be concerned
to close the file manually.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="s">"foo"</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>
<span class="c1"># bad practice
</span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"./data.csv"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"some data"</span><span class="p">)</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="s">"bar"</span><span class="p">]</span> <span class="c1"># KeyError
# f.close() never executes which leads to memory issues
</span>
<span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="c1"># good practice
</span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"./data.csv"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"some data"</span><span class="p">)</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="s">"bar"</span><span class="p">]</span>
<span class="c1"># python still executes f.close() even if the KeyError exception occurs
</span></code></pre></div></div>
<h2 id="11-using-set-for-searching-instead-of-a-list">11. Using set for searching instead of a list</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">s</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span><span class="s">'s'</span><span class="p">,</span> <span class="s">'p'</span><span class="p">,</span> <span class="s">'a'</span><span class="p">,</span> <span class="s">'m'</span><span class="p">])</span>
<span class="n">l</span> <span class="o">=</span> <span class="p">[</span><span class="s">'s'</span><span class="p">,</span> <span class="s">'p'</span><span class="p">,</span> <span class="s">'a'</span><span class="p">,</span> <span class="s">'m'</span><span class="p">]</span>
<span class="c1"># ok for small no. of elements
</span><span class="k">def</span> <span class="nf">lookup_list</span><span class="p">(</span><span class="n">l</span><span class="p">):</span>
<span class="k">return</span> <span class="s">'s'</span> <span class="ow">in</span> <span class="n">l</span> <span class="c1"># O(n)
</span>
<span class="c1"># better for large no. of elements
</span><span class="k">def</span> <span class="nf">lookup_set</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="k">return</span> <span class="s">'s'</span> <span class="ow">in</span> <span class="n">s</span> <span class="c1"># O(1)
</span></code></pre></div></div>
<p>Sets are implemented using hash in python, which makes searching of element faster(O(1)) as compared to searching in a
list(O(n)).</p>
<h2 id="12-using--while-importing-a-module">12. using * while importing a module</h2>
<p>Imports should always be specific. Importing * from a module is a very bad practice that pollutes the namespace.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># bad practice
</span><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">ceil</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="c1"># good practice
</span><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">ceil</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">ceil</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># we know where ceil comes from
</span></code></pre></div></div>
<h2 id="13-using-items-for-iterating-a-dictionary">13. using items() for iterating a dictionary</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">d</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"name"</span><span class="p">:</span> <span class="s">"Aarya"</span><span class="p">,</span>
<span class="s">"age"</span><span class="p">:</span> <span class="mi">13</span>
<span class="p">}</span>
<span class="c1"># Dont do this
</span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">key</span><span class="si">}</span><span class="s"> = </span><span class="si">{</span><span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="c1"># Instead do this
</span><span class="k">for</span> <span class="n">key</span><span class="p">,</span><span class="n">val</span> <span class="ow">in</span> <span class="n">d</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">key</span><span class="si">}</span><span class="s"> = </span><span class="si">{</span><span class="n">val</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="sources">Sources</h2>
<ul>
<li><a href="https://realpython.com/python-f-strings/">RealPython f-strings</a></li>
<li><a href="https://realpython.com/the-most-diabolical-python-antipattern/">RealPython the most diabolic anti pattern</a></li>
<li><a href="https://arielortiz.info/s201911/pycon2019/docs/design_patterns.html">Python Idiom Patterns</a></li>
<li><a href="https://towardsdatascience.com/18-common-python-anti-patterns-i-wish-i-had-known-before-44d983805f0f">18 Common Python Anti-Patterns I Wish I Had Known Before</a></li>
</ul>A collection of Python code best practices. Read to check how many of these you already know!\#1 Python Anti-Pattern - Mutable Default Arguments2021-10-29T00:00:00+05:302021-10-29T00:00:00+05:30https://dollardhingra.com/blog/python-mutable-default-arguments<h2 id="introduction">Introduction</h2>
<p>If you are not sure what a mutable default argument is, please read the full article as it can save you hours of
debugging.</p>
<h2 id="code-example-with-a-mutable-default-argument">Code Example With A Mutable Default Argument</h2>
<p>Consider the following code example below.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">add_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">,</span> <span class="n">box</span><span class="o">=</span><span class="p">[]):</span>
<span class="n">box</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">box</span>
</code></pre></div></div>
<p>Let’s understand step by step what is happening:</p>
<ul>
<li>We are creating a function to add fruits(str) in a box(list)</li>
<li>There is a <code class="language-plaintext highlighter-rouge">add_fruit</code> function which is responsible for adding the <code class="language-plaintext highlighter-rouge">fruit</code></li>
<li>This function takes 2 arguments: <code class="language-plaintext highlighter-rouge">fruit</code> and <code class="language-plaintext highlighter-rouge">box</code></li>
<li><strong>Attention!</strong> : The second argument here is a mutable default argument.</li>
</ul>
<h2 id="so-what-is-mutable-default-argument">So, what is mutable default argument?</h2>
<p>An argument in a function with default value as mutable.</p>
<p>In short, Python has both mutable and immutable types. The difference being:</p>
<ul>
<li>mutables can be modified</li>
<li>immutables can’t be modified.</li>
</ul>
<p>For eg: Tuple is an immutable type. If we define a tuple like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">weekends</span> <span class="o">=</span> <span class="p">(</span><span class="s">'saturday'</span><span class="p">,</span> <span class="s">'sunday'</span><span class="p">,)</span>
<span class="n">weekends</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s">'Monday'</span> <span class="c1"># TypeError: 'tuple' object does not support item assignment
</span></code></pre></div></div>
<p>An immutable type can never be modified.</p>
<h2 id="what-you-might-expect">What You Might Expect</h2>
<p>let’s modify our code and create a couple of boxes, i.e. red box and yellow box</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">add_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">,</span> <span class="n">box</span><span class="o">=</span><span class="p">[]):</span>
<span class="n">box</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">box</span>
<span class="n">red_box</span> <span class="o">=</span> <span class="n">add_fruit</span><span class="p">(</span><span class="s">"apple"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"red box: </span><span class="si">{</span><span class="n">red_box</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">yellow_box</span> <span class="o">=</span> <span class="n">add_fruit</span><span class="p">(</span><span class="s">"mango"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"yellow box: </span><span class="si">{</span><span class="n">yellow_box</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="expected-output">Expected Output</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>red box: ["apple"]
yellow box: ["mango"]
</code></pre></div></div>
<h3 id="actually-output">Actually Output</h3>
<p>Actually, you get the following output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>red box: ["apple"]
yellow box: ["apple", "mango"]
</code></pre></div></div>
<p>Wait? What? We never added apple in the yellow box.</p>
<h2 id="what-exactly-happened">What Exactly Happened</h2>
<p>A new list is created once when the function is defined, and the same list is used in each successive call.</p>
<blockquote>
<p>Python’s default arguments are evaluated once when the function is defined. This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.</p>
</blockquote>
<p>We will get the same result for other mutable types also(For eg: dict).</p>
<h2 id="what-should-be-done">What Should Be Done</h2>
<p>If your function needs to have a default argument for a mutable type, then default it with None and also add a check for same.
Let’s modify our <code class="language-plaintext highlighter-rouge">add_fruit</code> function:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">add_fruit</span><span class="p">(</span><span class="n">fruit</span><span class="p">,</span> <span class="n">box</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">box</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">box</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">box</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">fruit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">box</span>
<span class="n">red_box</span> <span class="o">=</span> <span class="n">add_fruit</span><span class="p">(</span><span class="s">"apple"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"red box: </span><span class="si">{</span><span class="n">red_box</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">yellow_box</span> <span class="o">=</span> <span class="n">add_fruit</span><span class="p">(</span><span class="s">"mango"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"yellow box: </span><span class="si">{</span><span class="n">yellow_box</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>
<p>This extra check can saves hours of debugging!</p>
<h2 id="conclusion">Conclusion</h2>
<p>It’s always a best practice to not use mutable default arguments. Instead, try adding an extra comparison check with
None to handle default arguments which are mutable.</p>This article talks about what mutable default arguments and why you should avoid using it for most of the casesCode Quality Tools in Python2021-09-12T00:00:00+05:302021-09-12T00:00:00+05:30https://dollardhingra.com/blog/python-code-quality-tools<h2 id="what-is-code-quality">What is code quality?</h2>
<p>Before we talk about code quality tools, let’s see what code quality is. We can agree that code is of high quality if</p>
<h3 id="it-does-what-it-is-supposed-to-do">It does what it is supposed to do</h3>
<p>If the code written is not doing what is supposed to do, then it doesn’t meet the very basic requirements, and we can
say that the code quality is low.</p>
<h3 id="it-does-not-contain-defects-or-problems">It does not contain defects or problems</h3>
<p>Let’s say that your code does what it is supposed to do, but struggles to perform well on edge cases. Let’s take an
example of a mobile app that you start using for communicating with friends and family. You can send & receive messages easily from your friends through this app. One fine day, you want to share a photo with a group and your app
crashes. Certainly, there is some issue with the code that has not been tested properly.</p>
<h3 id="it-is-easy-to-read-maintain-and-extend">It is easy to read, maintain, and extend</h3>
<p>Robert C. Martin in his book <a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/">Clean Code</a> says:</p>
<blockquote>
<p>Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as
part of the effort to write new code. Because this ratio is so high, we want the reading of the code to be easy,
even if it makes the writing harder.</p>
</blockquote>
<p>Therefore, code readability is a very important factor in code quality. A code which is readable makes it easier to maintain and extend.</p>
<h2 id="style-guide-following-the-conventions">Style guide: following the conventions</h2>
<blockquote>
<p>Great codebases look like they were written by an individual,
when they were worked on by a team.</p>
</blockquote>
<p>Consistent code is easier to read & therefore maintain by a team.
<strong>A style guide serves the purpose of defining a consistent way to write your code.</strong>
The style guide contains conventions to be followed for writing a code.
<a href="https://pep8.org/">PEP8</a> is the official style guide for Python that is recommended to use for writing any python code.
Let’s consider a small example. In the python code below, the operators tend to get scattered across different columns
on the screen, and each operator is moved away from its operand and onto the previous line.
Here, the eye has to do extra work to tell which items are added and which are subtracted:</p>
<p><strong>Not Recommended:</strong></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## No: operators sit far away from their operands
</span><span class="n">income</span> <span class="o">=</span> <span class="p">(</span><span class="n">gross_wages</span> <span class="o">+</span>
<span class="n">taxable_interest</span> <span class="o">+</span>
<span class="p">(</span><span class="n">dividends</span> <span class="o">-</span> <span class="n">qualified_dividends</span><span class="p">)</span> <span class="o">-</span>
<span class="n">ira_deduction</span> <span class="o">-</span>
<span class="n">student_loan_interest</span><span class="p">)</span>
</code></pre></div></div>
<p>To solve this readability problem, mathematicians and their publishers follow the opposite convention which is more
readable</p>
<p><strong>Recommended:</strong></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## Yes: easy to match operators with operands
</span><span class="n">income</span> <span class="o">=</span> <span class="p">(</span><span class="n">gross_wages</span>
<span class="o">+</span> <span class="n">taxable_interest</span>
<span class="o">+</span> <span class="p">(</span><span class="n">dividends</span> <span class="o">-</span> <span class="n">qualified_dividends</span><span class="p">)</span>
<span class="o">-</span> <span class="n">ira_deduction</span>
<span class="o">-</span> <span class="n">student_loan_interest</span><span class="p">)</span>
</code></pre></div></div>
<p>Another official recommendation from Python is <a href="https://www.python.org/dev/peps/pep-0257/">PEP-257</a> Docstring Conventions.
A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">complex</span><span class="p">(</span><span class="n">real</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">imag</span><span class="o">=</span><span class="mf">0.0</span><span class="p">):</span>
<span class="s">"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""</span>
<span class="k">if</span> <span class="n">imag</span> <span class="o">==</span> <span class="mf">0.0</span> <span class="ow">and</span> <span class="n">real</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">:</span>
<span class="k">return</span> <span class="n">complex_zero</span>
<span class="p">...</span>
</code></pre></div></div>
<p>There are tools capable of generating documentation directly from the code if the docstrings are consistent in the code.</p>
<p>Some organisations like Google have their own <a href="https://google.github.io/styleguide/">style guides</a>.
Checkout google’s <a href="https://google.github.io/styleguide/pyguide.html">python style guide</a>.</p>
<h2 id="how-to-measure-code-quality">How to measure code quality?</h2>
<p>There are different ways to measure code quality in general. This article talks about some basic tools
provided in Python to measure the code quality:</p>
<ul>
<li>Linters</li>
<li>Formatters</li>
</ul>
<h2 id="linters">Linters</h2>
<p>Linters analyze code to detect various categories of issues:</p>
<ol>
<li>Logical Issue
<ul>
<li>Code errors</li>
<li>Code with potentially unintended results</li>
<li>Dangerous code patterns</li>
</ul>
</li>
<li>Stylistic Issue
<ul>
<li>Code not conforming to defined conventions/style guide</li>
</ul>
</li>
</ol>
<h3 id="popular-python-linters">Popular python linters</h3>
<ol>
<li><a href="https://www.pylint.org/">Pylint</a>: Features like checking <strong>coding standards</strong>, <strong>error detection</strong>, <strong>refactoring help</strong> and more.</li>
<li><a href="https://github.com/PyCQA/pycodestyle">pycodestyle</a>: for checking some of the style conventions in PEP8</li>
<li><a href="https://pypi.org/project/flake8/">Flake8</a>: a combination of following linters: <a href="https://github.com/PyCQA/pyflakes">PyFlakes</a>,
<a href="https://github.com/PyCQA/pycodestyle">pycodestyle</a>, <a href="https://github.com/PyCQA/mccabe">Ned Batchelder’s McCabe script</a></li>
<li><a href="https://github.com/klen/pylama">Pylama</a>: composed of the following linters and other tools for analyzing code:
<ul>
<li><a href="https://github.com/PyCQA/pycodestyle">pycodestyle</a></li>
<li><a href="https://github.com/PyCQA/pydocstyle">pydocstyle</a></li>
<li><a href="https://github.com/PyCQA/pyflakes">PyFlakes</a></li>
<li><a href="https://github.com/PyCQA/mccabe">Mccabe</a></li>
<li><a href="https://www.pylint.org/">Pylint</a></li>
<li><a href="https://radon.readthedocs.io/en/latest/">Radon</a></li>
<li><a href="https://atom.io/packages/linter-gjslint">gjslint</a></li>
</ul>
</li>
</ol>
<p>Every linter has its advantages and disadvantages and should be picked up based on the project you are working on.
If a linter doesn’t give out any warning for any lint, that doesn’t mean that code is always correct, a linter is just a
static tool for code analysis. It cannot check if the job that is supposed to be done is done or not.</p>
<h2 id="formatters">Formatters</h2>
<p>Formatters automatically format your code based on a style guide. Some popular python formatters are:</p>
<h3 id="black">Black</h3>
<p>as per the black’s documentation, <a href="https://github.com/psf/black">Black</a> is “The uncompromising Python code formatter”.
It is my personal favourite because it has minimal configuration and is fast enough. Black is used by some very popular open-source projects, such as pytest, tox, Pyramid, Django Channels, Poetry, and so on. Example usage:</p>
<p><code class="language-plaintext highlighter-rouge">unique.py</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">is_unique</span><span class="p">(</span>
<span class="n">s</span>
<span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">s</span>
<span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
<span class="k">if</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span>
<span class="n">is_unique</span><span class="p">(</span><span class="nb">input</span><span class="p">())</span>
<span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">$ black unique.py</code> produces the following</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">is_unique</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
<span class="k">if</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">is_unique</span><span class="p">(</span><span class="nb">input</span><span class="p">()))</span>
</code></pre></div></div>
<h3 id="yapf">YAPF</h3>
<p><a href="https://github.com/google/yapf">YAPF</a> (Yet Another Python Formatter) is Google’s official python formatter which
follows google’s <a href="https://google.github.io/styleguide/pyguide.html">style guide</a>. The documentation is easy to
understand the installation and configuration for this formatter.</p>
<h3 id="autopep8">autopep8</h3>
<p><a href="https://pypi.org/project/autopep8/">autopep8</a> is an unofficial, yet popular, tool that automatically formates
Python code to conform to PEP 8. It uses pycodestyle, Python’s official PEP-8 violation checker tool, to determine what parts of the code need to be formatted.</p>
<h3 id="isort">isort</h3>
<p><a href="https://github.com/PyCQA/isort">isort</a> is a Python utility/library to sort imports alphabetically, and
automatically separated into sections and by type.</p>
<p>Before isort:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object3</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object2</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="n">lib15</span><span class="p">,</span> <span class="n">lib1</span><span class="p">,</span> <span class="n">lib2</span><span class="p">,</span> <span class="n">lib3</span><span class="p">,</span> <span class="n">lib4</span><span class="p">,</span> <span class="n">lib5</span><span class="p">,</span> <span class="n">lib6</span><span class="p">,</span> <span class="n">lib7</span><span class="p">,</span> <span class="n">lib8</span><span class="p">,</span> <span class="n">lib9</span><span class="p">,</span> <span class="n">lib10</span><span class="p">,</span> <span class="n">lib11</span><span class="p">,</span> <span class="n">lib12</span><span class="p">,</span> <span class="n">lib13</span><span class="p">,</span> <span class="n">lib14</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="n">lib3</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Hey"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"yo"</span><span class="p">)</span>
</code></pre></div></div>
<p>After isort:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="p">(</span><span class="n">lib1</span><span class="p">,</span> <span class="n">lib2</span><span class="p">,</span> <span class="n">lib3</span><span class="p">,</span> <span class="n">lib4</span><span class="p">,</span> <span class="n">lib5</span><span class="p">,</span> <span class="n">lib6</span><span class="p">,</span> <span class="n">lib7</span><span class="p">,</span> <span class="n">lib8</span><span class="p">,</span>
<span class="n">lib9</span><span class="p">,</span> <span class="n">lib10</span><span class="p">,</span> <span class="n">lib11</span><span class="p">,</span> <span class="n">lib12</span><span class="p">,</span> <span class="n">lib13</span><span class="p">,</span> <span class="n">lib14</span><span class="p">,</span> <span class="n">lib15</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object</span><span class="p">,</span> <span class="n">Object2</span><span class="p">,</span> <span class="n">Object3</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Hey"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"yo"</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="where-to-use-the-code-quality-tools">Where to use the code quality tools?</h2>
<p>You can use these code quality tools:</p>
<ul>
<li>In your IDE/editor, when you are writing your code</li>
<li>on committing the code</li>
<li>merging the code or while running the tests</li>
</ul>
<h3 id="ideeditor">IDE/editor</h3>
<p>Most modern IDEs and editors have inbuilt support for linters. Some editors like <a href="https://code.visualstudio.com/docs/python/linting">VS Code</a> have great support for linters. You can also run a specific formatter on running the auto-format command in VS Code. Check more about it
<a href="https://code.visualstudio.com/docs/python/editing#_formatting">here</a>.</p>
<p>For IDE like Pycharm, there are plugins available for running lints and formatters. For example, check out the <a href="https://plugins.jetbrains.com/plugin/11084-pylint">pylint plugin</a> and
<a href="https://plugins.jetbrains.com/plugin/10563-black-pycharm">black formatter plugin</a></p>
<h3 id="on-committing-the-code-hooks">On Committing the code: Hooks</h3>
<p>Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur through <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">git-hooks</a>. You can use these scripts to run the
lints and formatters to block any new code that doesn’t meet quality standards or to automatically format the commit.
A useful <a href="https://pre-commit.co m/">pre-commit</a> (written in python) is a multi-language package manager for
pre-commit hooks. You specify a list of hooks you want and pre-commit manages the installation and execution of any hook written in any language before every commit</p>
<h3 id="when-running-tests-continuous-integration">When running tests: Continuous Integration</h3>
<p>You can also place linters & formatters directly into whatever system you may use for continuous integration.
The linters can be set up to fail the build if the code doesn’t meet quality standards. A CI(continuous integration) is a practice of automating the integration of code changes from multiple contributors into a single software project. This means that as soon as a developer merge their code in the main branch, the CI
pipeline(for eg: git workflow) can perform some automatic operations like formatting before the code is deployed.</p>
<h2 id="conclusion">Conclusion</h2>
<p>A code is of high quality if it does what it is supposed to do, it <strong>does not contain defects</strong> or problems and
it is easy to <strong>read, maintain, and extend</strong>. Style guides like A style guide like <a href="https://pep8.org/">PEP8</a> and google’s <a href="https://google.github.io/styleguide/">style guide</a> help serve the purpose of defining a consistent way to write your code. The different tools for measuring and improving the code quality are linters & formatters.</p>
<p>Linters analyze code to detect various categories of issues like logistical issue and stylistic issues. Some<br />
popular linters are <a href="https://www.pylint.org/">Pylint</a>, <a href="https://github.com/PyCQA/pycodestyle">pycodestyle</a>,
<a href="https://pypi.org/project/flake8/">Flake8</a> and <a href="https://github.com/klen/pylama">Pylama</a>.</p>
<p>Formatters automatically format your code based on a style guide. Some popular formatters are
<a href="https://github.com/psf/black">Black</a>, <a href="https://github.com/google/yapf">YAPF</a>,
<a href="https://pypi.org/project/autopep8/">autopep8</a> and <a href="https://github.com/PyCQA/isort">isort</a>.</p>
<p>Finally, You can use these code quality tools using:</p>
<ul>
<li>IDE/editors - when you are writing your code</li>
<li>pre-commit hooks - on committing the code using</li>
<li>continuous integration - while running the tests</li>
</ul>An introduction to some code quality tools in python like linters, formatters & style guidesCoWIN Vaccine Availability - My first open source project2021-05-17T00:00:00+05:302021-05-17T00:00:00+05:30https://dollardhingra.com/blog/cowinquick-vaccine-availability<h1 id="introduction">Introduction</h1>
<p><a href="https://www.cowin.gov.in/home">CoWIN</a> is a vaccination booking platform for booking vaccination for Indian citizens. This is the largest vaccination drive ever. Anyone can simply go and book the vaccination slots as per the availability in the respective state and districts. In this post, I have created another platform just for checking vaccine availability.</p>
<blockquote>
<p>But what’s the need for creating a different platform for checking the availability when you already have the official platform?</p>
</blockquote>
<h1 id="problem">Problem</h1>
<p>I had a hard time finding the vaccination slots (for the 18-45 age group). Some of the problems I(and other people I think) faced:</p>
<ul>
<li>Most of the times the slots were not available and when they were available, they were booked in seconds.</li>
<li>The User experience of the original CoWIN portal is not that great. Firstly you need to log in through your phone number(by an OTP which sometimes refuses to arrive on your phone). You are automatically logged out after 15-20 mins.</li>
<li>Some features are missing but can help find the slots. For eg: The ability to search across multiple districts.</li>
<li>A filter for hiding the unavailable slots from the search result would have been really helpful.</li>
</ul>
<blockquote>
<p>Some people have also written scripts for notifying whenever the slots are available. The idea is great but there are a couple of problems with the script’s approach:
As soon as you get the notification that slots are available in your district, you open up your browser ->login into the <a href="https://www.cowin.gov.in/home">CoWIN portal</a> -> select the beneficiaries that you want to get vaccinated -> select the state & district/enter pin code and click search -> BOOM! the slots have already been booked!</p>
</blockquote>
<h1 id="solution">Solution</h1>
<p>I created a simple HTML page that calls the public CoWIN API in JS. Check out the live demo <a href="https://dollardhingra.com/cowinquick/">here</a>.<br />
<strong>Note:</strong> This website can only be used for checking the available slots. For booking, the <a href="https://www.cowin.gov.in/home">official site</a> must be used.</p>
<iframe width="1046" height="588" src="https://www.youtube.com/embed/knjqHZNs7LM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>This helps in booking as</p>
<ul>
<li>There is no need for entering phone number/OTP again and again</li>
<li>The results are visible for multiple districts at once.</li>
</ul>
<h1 id="how-i-have-booked-more-than-20-slots-till-now">How I have booked more than 20 slots till now</h1>
<p>I have booked more than 20 slots(for friends and family) with the following approach:</p>
<ul>
<li>
<p>There is usually a 1-2 hour window when the slots are open. Usually between 6-10 pm on weekdays and 12-4 pm on weekends. These timings may change so keep casually looking for the slots whenever you have your phone in hand through the <a href="https://dollardhingra.com/cowinquick/">cowinquick site</a>.</p>
</li>
<li>
<p>Let’s say that you find available slots in your district or a nearby district. Note the name of the centre. Let’s say that the name of the centre is “BLK Hospital Site 1”.</p>
</li>
<li>
<p>In one tab, login into the <a href="https://www.cowin.gov.in/home">cowin portal</a> and select the city and district/pin code. In the other tab, you can open the <a href="https://dollardhingra.com/cowinquick/">cowinquick site</a>.</p>
</li>
<li>
<p>Now check if “BLK Hospital Site 1” is available. If not, then come back to the <a href="https://dollardhingra.com/cowinquick/">cowinquick site</a> and search again. There is a high probability that “BLK Hospital Site 2” will come up next in few minutes.</p>
</li>
<li>
<p>Keep searching after every 5-10 seconds. There is a chance that you are searching in the time window where the slots are being updated. You will come to know that the slots are being updated if you keep searching for 5-10 mins(clicking search after every 10-20 seconds). Either of the following will happen:</p>
<ul>
<li>
<p>You will see that for some centres the slots are being open and booked in 15-20 seconds. Try booking that centre in those 15 seconds. The advantage you have on <a href="https://dollardhingra.com/cowinquick/">cowinquick site</a> is that you can see the results for multiple districts at once & you don’t need to select 18+ again on every search.</p>
</li>
<li>
<p>There may be a situation when you don’t see any centres updating slots. You can then try the same after some time.</p>
</li>
</ul>
</li>
</ul>One of my side projects for booking vaccine slots quicklyBeginner’s guide to abstract base class in Python2021-03-25T00:00:00+05:302021-04-22T22:20:02+05:30https://dollardhingra.com/blog/python-abstract-base-class<h2 id="introduction">Introduction</h2>
<p>In this article, you will learn about the benefits of abstract base classes in Python and how to use them with Python’s built in <code class="language-plaintext highlighter-rouge">abc</code> module</p>
<figure class="">
<img src="/assets/images/blog/abstract%20base%20class/abstract_base_classes_cover.png" alt="Abstract Base Classes in Python" /></figure>
<h2 id="what-is-an-abstract-base-class">What is an abstract base class?</h2>
<p>In simple words, an abstract base class provides a blueprint for other classes. Abstract base classes don’t contain the implementation. Instead, they provide an interface and make sure that the derived classes are properly implemented.</p>
<h2 id="why-do-we-need-abstract-base-classes">Why do we need abstract base classes?</h2>
<p>Imagine that you are creating a game in which you have different animals. For defining animals you can have an abstract class called <code class="language-plaintext highlighter-rouge">Animal</code>. A Dog/Cat/Duck are all the classes that are derived from the base class <code class="language-plaintext highlighter-rouge">Animal</code>.</p>
<h3 id="animal-base-class">Animal Base Class</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Animal</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">get_age</span><span class="p">():</span>
<span class="c1">## return age of the animal
</span>
<span class="k">def</span> <span class="nf">is_dead</span><span class="p">():</span>
<span class="c1">## return the colour of the animal
</span></code></pre></div></div>
<ul>
<li>Every animal will have both of these methods. So, we want every animal to implement these methods whenever we are inheriting an animal class from the <code class="language-plaintext highlighter-rouge">Animal</code> class. We cannot have an animal that doesn’t support any of these methods. [Rule 1]</li>
<li>Whenever we see an animal, we call it a dog/cat/duck/tiger. These are all kinds of <strong>animals</strong>. That is, you never look at something purple and furry and say “that is an animal and there is a no more specific way of defining it”. The point is, that you can never see an animal walking around that isn’t more specifically something else (duck, pig, etc.) [Rule 2]</li>
</ul>
<h2 id="rules">Rules</h2>
<p>By using these 2 rules we can make our code more maintainable and programmer-friendly</p>
<h3 id="rule-1">Rule 1</h3>
<blockquote>
<p>Subclasses inherited from a specific base class must implement all the methods and properties defined in the abstract base class.</p>
</blockquote>
<h3 id="rule-2">Rule 2</h3>
<blockquote>
<p>Abstract base classes cannot be instantiated. They are inherited by the other subclasses.</p>
</blockquote>
<h2 id="implementation-in-python">Implementation in Python</h2>
<p>Let’s try to implement these animal classes in Python with the rules we talked about.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Animal</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">get_age</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># return the animal's age
</span> <span class="k">raise</span> <span class="nb">NotImplementedError</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">is_dead</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># check if animal is dead or alive
</span> <span class="k">raise</span> <span class="nb">NotImplementedError</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">Dog</span><span class="p">(</span><span class="n">Animal</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">bark</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"whoof whoof!!"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_age</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"5 years"</span><span class="p">)</span>
</code></pre></div></div>
<p>In the above program, we are creating a <code class="language-plaintext highlighter-rouge">Dog</code> class that is inheriting all the methods from the <code class="language-plaintext highlighter-rouge">Animal</code> class. Let’s see if rule 1 is followed.</p>
<blockquote>
<p>Rule 1: Subclasses inherited from a specific base class must implement all the methods and properties defined in the abstract base class.</p>
</blockquote>
<p>Let’s create an object of the <code class="language-plaintext highlighter-rouge">Dog</code> class</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bulldog</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">()</span>
<span class="n">bulldog</span><span class="p">.</span><span class="n">get_age</span><span class="p">()</span> <span class="c1"># 5 years
</span>
<span class="n">bulldog</span><span class="p">.</span><span class="n">is_dead</span><span class="p">()</span> <span class="c1"># NotImplementedError
</span>
</code></pre></div></div>
<p>The subclass <code class="language-plaintext highlighter-rouge">Dog</code> does not implement the <code class="language-plaintext highlighter-rouge">is_dead</code> method of the <code class="language-plaintext highlighter-rouge">Animal</code> base class and when we try to call this method from the bulldog object we get an error. So this works as expected. But we can run the program successfully if we don’t call this method without even a warning for breaking this rule. Ideally, all the methods of the base class should be implemented by the subclass.</p>
<p><strong>The above program does not follow Rule 1.</strong></p>
<blockquote>
<p>Rule 2: Abstract base classes cannot be instantiated. They are inherited by the other subclasses.
Let’s check if Rule 2 is followed. Let’s try to instantiate the base class.</p>
</blockquote>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">animal</span> <span class="o">=</span> <span class="n">Animal</span><span class="p">()</span>
<span class="n">animal</span><span class="p">.</span><span class="n">get_age</span><span class="p">()</span> <span class="c1"># NotImplementedError
</span></code></pre></div></div>
<p>We get the error if we try to call the base class’ method. But, we can instantiate the base class.
<strong>So, the above program doesn’t follow Rule 2</strong></p>
<h2 id="abc-module-to-the-rescue"><code class="language-plaintext highlighter-rouge">abc</code> module to the rescue</h2>
<p>With Python’s <code class="language-plaintext highlighter-rouge">abc</code> module, we can do better and make sure that both the rules are followed.
Let’s update the above program</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
<span class="o">@</span><span class="n">abstractmethod</span>
<span class="k">def</span> <span class="nf">get_age</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">pass</span>
<span class="o">@</span><span class="n">abstractmethod</span>
<span class="k">def</span> <span class="nf">is_dead</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">Dog</span><span class="p">(</span><span class="n">Animal</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">bark</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"whoof whoof!!"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_age</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"5 years"</span><span class="p">)</span>
<span class="c1"># we forgot to declare "is_dead" again
</span>
</code></pre></div></div>
<p>In the above program, we have imported the helper class <code class="language-plaintext highlighter-rouge">ABC</code> from the module <code class="language-plaintext highlighter-rouge">abc</code> through which we have implemented the base class. Also, we have used the decorator <code class="language-plaintext highlighter-rouge">@abstractmethod</code> for all the methods in our base class.</p>
<p>Let’s try to instantiate our subclass: <code class="language-plaintext highlighter-rouge">Dog</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bulldog</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">()</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>TypeError: Can't instantiate abstract class Dog with abstract methods is_dead
</code></pre></div></div>
<p>This time we get a <code class="language-plaintext highlighter-rouge">TypeError</code> specifying that the abstract method is not implemented.</p>
<p><strong>This follows Rule 1 successfully.</strong></p>
<p>Let’s try to instantiate our base class: <code class="language-plaintext highlighter-rouge">Animal</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">animal</span> <span class="o">=</span> <span class="n">Animal</span><span class="p">()</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>TypeError: Can't instantiate abstract class Animal with abstract methods get_age, is_dead
</code></pre></div></div>
<p>This time we get a <code class="language-plaintext highlighter-rouge">TypeError</code> specifying that the abstract class cannot be instantiated.</p>
<p><strong>This follows Rule 2 successfully.</strong></p>
<h2 id="conclusion">Conclusion</h2>
<p>Using ABCs in your code will make your class hierarchies more robust and more readily maintainable. Some of the key takeaways in this article are:</p>
<ul>
<li>Abstract e Classes(ABCs) ensure that derived classes implement the particular methods from the base class at instantiation time.</li>
<li>Using ABCs can help in writing bug-free class hierarchies which are easy to read and maintain.</li>
</ul>In this article, you will learn about the benefits of abstract base classes and how to use them with python's built in abc module