Escapes

Writing is my best escape from analysis paralysis.

Writing Aesthetic Python Code (PEP 8)

Why bother about making your code look good when it works just fine?
Consider why you care about your handwriting, and it might get you thinking till the time I answer it for you.
I’m one of those who get triggered and feel anxious on seeing the minutest noncompliance to styling and following proper code style, up to the point of missing even a single space here or there. For those who want to save themselves from such taunts from people like me, dive in!

(You can also read this on Medium)

Aesthetic picture

. . .

What is code style?

Readable code

One thing that I’d like to clarify in the very beginning is that following code style is not exactly the same as writing readable code. Though making your code readable does include following the proper styling conventions also, readability deals more with writing more understandable code, rather than better looking code. Adding more comments, structuring the code well or using understandable and self-explanatory function/variable names are some of the aspects to consider to make your code readable.

Code style

In short, code style is how your code looks. This includes following some basic conventions while writing your code. These styling conventions are often laid out either officially by the language organization itself or otherwise by big communities, keeping common-practices in mind. Formatting your code to comply with these set of guidelines is what code style means.

. . .

Why bother about styling code? (Independent of language)

One very important (and believe me when I stress on important) lesson that I learned from working on Open-Source projects, where a large number of people collaborate and work on the same codebase is to use a universal convention. There might be hundreds of people who would have worked on the same file. In such a scenario, consistency is key. Imagine everyone writing to the same file with their own style and formatting. It’ll not take much time for things to go haywire and the code becoming a nightmare for the next person coming in to read, maintain or edit it. For the code to be easy to read for everyone, styling conventions are a must.

Not just true for open-source, but any large codebase is bound to be worked upon by a large number of people, and it is almost inevitable that you’ll work on such codebases at some point of your developer journey. Well formatted code is not just pleasing to the eye, but also expressive and easy to understand for you yourself and others.

Now with the advancements in Dev-Ops, many CI tools reject code that diverts from the code style guidelines outright. So, for your code to be accepted in the first place, you need it to be properly styled! This makes it evident that good-looking and properly formatted code is no less important than correct logic and working.

It bugs me immensely when I find any code, anywhere, written by anyone which doesn’t follow the correct guidelines. Be it tutorials or even someone’s practice code. To make it a habit, one must ensure to follow the guidelines even while writing pen-paper code. I very often see newcomers, or sometimes even some experienced folks neglecting the importance of styling code properly if they’re not writing it for a big project or just writing for personal practice. This will just make you inconsistent and less habitual to writing good quality code subconsciously.

It’s better to mend your ways before someone else pinpoints your mistakes and you land them in such a situation:

A meme

. . .

Key takeaways from Python’s style guide (PEP 8)

As proposed in the official PEP 8 document, there are various conventions one should follow while writing Python code anywhere.

One of Guido’s key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code.

Here are some of the key takeaways from it that’ll make your code acceptable:

(For more details on a particular guideline, refer to the link in its heading.)

1. Indentation

  • Use spaces instead of tabs for indentation.
  • Use 4 spaces per indentation level.
  • Continuation lines should align wrapped elements with opening delimiter or use a hanging indent:
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# Add 4 spaces (an extra level of indentation)
# to distinguish arguments from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

This is a tricky guideline, and you might need to revisit the PEP several times to get the indentation levels and hanging indents right.

2. Maximum Line Length

  • Limit all lines to a maximum of 79 characters.
  • This is the one to take extra care of. Many other guidelines are shaped in order to be consistent with the maximum permissible line length.
  • Docstrings or Comments should be limited to 72 characters in a line.

3. Blank Lines

  • Surround top-level function and class definitions with two blank lines.
  • Method definitions inside a class are surrounded by a single blank line:
import math


# 2 blank lines above class definition
class foo:
    def a(self):
        # some code

    # one blank line between class methods
    def b(self):
        # some code
  • Use blank lines inside functions, sparingly, to indicate logical sections.

4. Source File Encoding

Always use UTF-8 file encoding (or ASCII in Python 2).

5. Imports

  • Use separate lines for separate imports:
# Correct:
import os
import sys
from subprocess import Popen, PIPE

# Wrong:
import sys, os
  • Imports are always put at the top of the file, just after any module comments and docstrings.
  • Star imports (from <module> import *) should be strictly avoided, as they make it unclear which names are present in the namespace.

6. Whitespaces in Expressions and Statements

This is a very important and easily the most neglected part according to me, so I highly recommend reading the link to the official document.

  • Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not).
  • Never use more than one space, and always have the same amount of whitespace on both sides of a binary operator:
i = 1 + 2
i += 1

"""
However, use spaces around operator with lowest priority
in case of multiple operators:
"""
i = x*x + y*y
  • Don’t use spaces around the = sign when used to indicate a keyword argument:
# Correct:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)
  • Never use more than one space around an assignment (or other) operator to align it with another:
# Correct:
x = 1
y = 2
long_variable = 3

# Wrong:
x             = 1
y             = 2
long_variable = 3
  • Add a whitespace after a comma, semicolon and colon (for type annotations), but not before them:
# Correct:
if x == 4: print x, y; x, y = y, x

i: str = "hello"

# Wrong:
if x == 4 : print x , y ; x , y = y , x

7. Comments

You might be wondering why to format comments, in fact that’s one thing you’re given liberty on. But the truth is, comments are somewhat more important than the code itself in expressing what your code does, and probably the first thing another person will read in your code. So you want to make sure another person has no problem skimming through your comments.

  • Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment).
  • Inline comments should be used less, but if used, separate the comment by at least two spaces from the statement. They should start with a # and a single space:
# This is Block comment.
print("Hello")  # 2 spaces after statement for inline comment
  • The complete conventions for formatting docstrings are compiled in PEP 257 (It’s a short document, so you might want to give it a quick read). The basic format for a docstring is:
"""This is a single line docstring.""""

"""Multi-line docstrings

Make sure that the ending triple quotes are on a separate line.
""""

8. Programming Recommendations

This section mentions the conventions to follow while writing logical parts to not only make them look good, but easy to understand. According to me, these violations when caught by linters are the hardest to locate and fix, so pay attention to these.

  • Use is not operator rather than not … is. For comparisons to singletons like None, always use is or is not, never the equality operators:
# Correct
if foo is not None:

# Wrong
if not foo is None:
  • When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause:
try:
    import platform_specific_module
except importerror:
    platform_specific_module = none
  • Don’t compare boolean values to True or False using ==:
# Correct:
if greeting:

# Wrong:
if greeting == True:

# Worse:
if greeting is True:
  • Never import modules or assign variables that are never used later on.


Though these are not all the guidelines, following these will avoid most of the common style errors caught by linters. If you encounter other errors, the official PEP 8 document is the best place to look for a solution.
If the guidelines seem too many to remember or a bit overwhelming, it’s nothing to worry about. It’s totally fine to refer the PEP repeatedly to check for the correct guidelines. After adopting them while coding anywhere (including practice or pen-paper coding), soon you’ll subconsciously start following the conventions without even realizing or making an extra effort.

. . .

Running compliance checks

Worry not, there’s no need for you to go through each line and column of your code to locate that one missing whitespace, or one line longer than 79 characters that’s not letting your code get accepted. Luckily, there are tools in existence that traverse your entire code, look for style violations, and report them with their exact location for you to fix (Just like error reporting!).
My personal favorites are flake8 and pycodestyle. Flake8 being a bit more verbose and strict is my go-to style checking tool and I make sure to run it on any Python code that I write before submitting it anywhere.
It’s as easy as running a single command to know what all needs to be fixed to comply to the correct PEP 8 guidelines and make your code look much better for one and all.

. . .

Before starting contributing to Open-Source projects, I wasn’t even aware that something like PEP 8 existed and I used to write horribly ugly Python code proudly. So you’re not expected to be born with this knowledge, but it’s better to start paying attention to code style sooner rather than later. For a perfect analogy, remember what I told you to think in the very beginning of this article and just consider yourself to be back in primary school, working on improving your handwriting. Just replace it with code! You know how to write (be it text or code), you just need to know how to make it look aesthetic.