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
Source: https://xkcd.com/1513/

The 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 will summarize some highlights that you can start applying to your code right away!

  • Maximum line length

  • Indentation

  • Whitespace and binary operators

  • Avoid extraneous whitespace

  • Write one statement per line

Maximum line length#

PEP 8 guides us to limit all lines to 79 characters max. 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 PEP 8 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

The backslash character (\) might be required to break a line when using more complicated statements such as the with statement (not covered during this course). You can find more examples in the PEP 8 documentation.

Indentation#

Indentation is an essential part of the Python code layout. As we already learned, for loops and conditional statements will not work correctly without consistent indentation. PEP 8 advices us to use 4 spaces per indentation level.

Let’s have a look at an 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 a single line is probably best, as it is not that long after all.

In addition, indentation is needed when breaking one command into multiple lines, such as in our example with the list us_cities above. In that case we used the implied line continuation inside the brackets. Following the 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 a list formatted this way.

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

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

Whitespace and binary operators#

According to PEP 8 we should surround binary operators with single space on either side.

We should 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#

We should also avoid having a space between the function name and parentheses when calling a function.

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

Write one statement per line#

For readability it is advised to avoid writing multiple statements on the same line.

# 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 find a 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 to make the code shorter, but perhaps they are also more difficult to read. Thus, PEP 8 recommends avoiding compound statements in general. However, sometimes squeezing multiple statements might your best option - you you just have to judge for yourself which option makes the code more readable and use 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 in this course, 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.