GCP 3 - Writing readable code

This week’s session on good programming practices focuses on writing code that is easy to read. During the previous lessons we have already discussed good practices regarding variable naming and describing your code using comments, which are also part of writing readable code. Here we focus on how to format the actual Python code to make it more readable.

Working code vs readable code

As you noticed, Python forces us to indent our code when writing for loops and conditional statements. Without the indentation, the code won’t work at all, or then it will not work as you would want it to work.

However, there are many cases in which you are able to write code that runs without errors, but you (or others!) might have a hard time reading it and understanding what the code actually does.

Ideally, our Python code would be understandable both for the computer and for humans reading it. Coding conventions are a set of generally agreed ways of writing programming code in a specific programming language. Coding conventions help programmers to write code that is consistent and easy to read. Consistency and readability are important for sharing your code with others, and also for helping your own brain to follow along!

xkcd: Code Quality https://xkcd.com/1513/

PEP 8 Style Guide

Readability counts.

The Zen of Python

The PEP 8 Style Guide for Python Code gives coding conventions that help us write code that is readable (by humans!) and consistent with code written by others.

PEP 8 goes far beyond the scope of what we have learned so far during this course, and we recommend that you re-visit the guidelines every now and then when learning new things. Here, we summarize some highlights that you can start applying to your code right away!

Maximum line length

PEP 8 guides us to limit all lines to max 79 characters: https://www.python.org/dev/peps/pep-0008/#maximum-line-length. Comments (multi-line or single line) should be limited to 72 characters.

Note

We use a line length limit of 88 characters on this website. The character limit according to PEP8 is 79 characters, but often a slightly longer line (up to 90 characters) helps with readability.

One of the guiding principles of Python is that Simple is better than complex, but sometimes you might end up having a line of code that exceeds 79 characters, for example, when defining lists.

Python is able to interpret the code correctly from multiple lines within parentheses, brackets and braces:

# Implicit line continuation inside brackets
us_cities = ["Detroit", "Chicago", "Denver", "Boston", 
            "Portland", "San Francisco", "Houston", "Orlando"]

Note: backslash (\) might be required to break a line when using more complicated statements such as the with statement (not covered during this course). See more examples in here.

Indentation

Indentation is an essential part of the Python code lay-out. As we already learned, for-loops and conditional statements won’t work correctly without indentation. PEP 8 advices us to use 4 spaces per indentation level.

Let’s have a look at our example with if-statements. The indented line tells Python what to do if the condition is True. Notice the 4 spaces in the indentation:

weather = "Rain"
wind = "Windy"

if (weather == "Rain") and (wind == "Windy"):
    print('Just stay at home')
Just stay at home

Following PEP 8, it is also possible to break the conditional expression into multiple lines if needed. Notice the extra parentheses:

if ((weather == "Rain") 
    and (wind == "Windy")):
    print("Just stay at home")
Just stay at home

To increase readability of this if-statement, we could add extra indetation to the continuation line of the conditional statement:

if ((weather == "Rain") 
        and (wind == "Windy")):
    print("Just stay at home")
Just stay at home

In our case, the first option with the conditional expression on one line is ok, as it is not that long afterall.

In addition, indentation is needed when breaking one command into multiple lines, such as in our example with the list us_cities above, where we used the implied line continuation inside the brackets. Following PEP 8 indentation guidelines, we can define us_cities also using a hanging indent. Note that there is no value on the first line of the list:

# Hanging indentation:
us_cities = [
    "Detroit", "Chicago", "Denver", "Boston", 
    "Portland", "San Francisco", "Houston", "Orlando"]

We will discuss more about indentations during week 4 when defining functions :)

Whitespace and binary operators

Surround binary operators with single space on either side. https://www.python.org/dev/peps/pep-0008/#other-recommendations

Do this always with:

  • assignment (=)

  • augmented assignment (+=, -= etc.)

  • comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not)

  • Booleans (and, or, not)

# yes:
i = 1
i = i + 1
i += 1
# no: 
i=1
i=i+1
i +=1

If using operators with different priorities, you can also do this:

# yes: 
a = 1
b = 2
c = (a+b) * (a-b)

Avoid extraneous whitespace

Avoid having a space between the function name and parenthesis when calling a function. https://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements

# yes:
print("Hello")
Hello
# no:
print ("Hello")
Hello

Write one statement per line

Avoid writing multiple statements on the same line: https://www.python.org/dev/peps/pep-0008/#other-recommendations

# yes:
print("Hello")
print("world")
Hello
world
# no:
print("Hello"); print("world")
Hello
world
# yes:
temperature = 17
if temperature > 25: 
    print(f"{temperature} is greater than 25")
# no:
temperature = 17
if temperature > 25: print(f"{temperature} is greater than 25")

Code readability versus code length?

You often have to balance between code readability and code length when writing efficient and readable code. Compound statements are a way of writing multiple statements on the same line making the code shorter, but perhaps also more difficult to read and thus PEP 8 recommends to avoid compound statements in general. However, sometimes squeezing multiple statements might be just allright - you you just have to judge yourself which option makes the code more readable and go for that.

List comprehensions

One puzzling example regarding this guideline is the use of list comprehensions when defining lists. List comprehensions are a useful approach for creating lists in a concise way. We are not covering list comprehensions during the lessons, but here is a short example from the Python documentation. In some cases, list comprehensions might make your code more readable and concise. In other cases, you might end up writing an excessively long statement which is difficult to read. Let’s have a look at two options that produce the same output:

Option A: This for loop iterates over a range, squares all values and appends them to a list

squares = []
for x in range(10):
    squares.append(x**2)

Option B: Square all values in the range using a list comprehension

squares = [x**2 for x in range(10)]

Both approaches are fine, and you are free to choose the option that you think makes the code more readable.