PEP8 — Coding style in Python


Python is having more and more success as a programming language, even outside the scientific and academic circles. When you want to implement a project, more and more often you can find native code written in this language. Being a fairly rich language, it is easy for every programmer to develop its own particular style, a way to report a comment, indentation of a function and so on. In the end, you have developed a style all your own … but is it correct?

The PEP 8 guidelines

Python is a language in developing and many new features are inserted gradually over the years. Python is at version 3.7 and has changed a lot from what Python was five years ago, or 10 years ago.

All language features are proposed, described and discussed in a series of official documents called PEP (Python Enhancement Proposals). Each of these documents has an identification number and a title that describes the topic discussed. You can see the latest list of all the PEP published by accessing PEP 0, that is the index of all PEPs.

Among these documents, there is one which has the task to collect and bring all the coding conventions for the proper implementation and easy to read a code written in Python. This document is called PEP 8 — Style guides for Python Code (see here) and since 2001 is continuously updated to be a valuable guide for all those who develop code with this language. Among the authors of this document is Guido Van Rossum, the creator of the Python language.

Among these documents, there is one which has the task to collect and bring all the coding conventions for the implementation of a code written in Python maintaining a correct style. This document is called PEP 8 — Style guides for Python Code (see here) and since 2001 it is continually updated so as to be a valuable guide for all those who develop code with this language. Among the authors of this document is Guido Van Rossum, the creator of the Python language.

Consistency as a basic principle

The key aspect that you must always keep in mind when you write the code is consistency. This aspect is very important when working within a project. You must make sure that all who take part in the project comply with the same style. PEP 8 has the purpose of defining a basic style to follow.

An interesting note to the document shows that consistency is such a strong principle that must be respected despite PEP8. In fact, if you should have to do with an existing code that does not respect at all any style rule, the guide to follow will be the one defined by the project (if it has one;)) and not the official one described in PEP8 and valid throughout the Python community.

Maximum line length

Many devices are limited to showing 80 characters per line. Then writing lines that contain a greater number of 79 characters, very often it leads to seeing on the screen dynamically truncated lines. This certainly does not facilitate the readability of everything. It is advisable to take advantage of the command syntax (eg a parenthesis) to change line maintaining a good level of readability. If this is not possible, then you can use the ‘\’ backslash character to change line.


You have to leave 4 blank spaces for each level of indentation.

if a > 2:
print a

Also when you have to deal with very long commands where there are brackets of various kinds, you have to span multiple lines. Again you have to put the indentation (inner indentation), keeping the code as readable as possible. You can do it, however, by creating indentations greater than 4 spaces. Doing this, you will not be confused with the following commands.

def a_function_too_long_to_be_in_one_line(
var_one, var two, var three,

Here in this case, the parameters of the function have been reported with an indentation of 6 spaces.

Blank lines

A good practice to follow is to separate the definitions of functions and classes with two blank lines than the rest of the code. It is also advisable to leave a blank line even before the definition of a method defined in a class. Instead inside a function or method, try to limit its use as much as possible.

Using spaces in expressions and statements

Another aspect described in detail within the guide is the use of the spaces within expressions or commands such as assignment statements. The indicated rules are simple to understand and follow.

Do not use spaces immediately before or after the parentheses or before punctuation

sum( array[ 2 ] , { value: 2 ] )        <strong>NO</strong>

It should be written in the following way:

sum(array[2], {value: 2}) <strong>YES</strong>

in which spaces are left only after punctuation such as commas and colons.

In expressions there must be only one space before and after the operator.

x &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= 1 <strong> NO</strong>

It should be written in the following way:

x = 1 <strong>YES </strong>
x = x — 1

But do not confuse ‘=’ character as the assignment that requires the space, with the case in which = will be used as an argument to a function with a default value.

The = does not want spaces in the statement of a function in which a default value is assigned to an argument.

def function(var1, var2 = 0.0): <strong>NO</strong>

It should be written in the following way:

def function(var1, var2=0.0) <strong>YES</strong>


First, regardless of how you write them, the comments have a very important role for the readability of the code. They must always be present and updated!

Since it is a written text, it is good practice to write the first character with a capital letter, unless you’re referring to a variable in your code.

Before you define a class or method, or a module, you use descriptive lengthy comments which occupy several lines. These comments are called block comments and even these must be properly indented. Each line in the comment starts with a # sign followed by a space. The paragraphs of the comments instead should be separated by blank lines that still begin with a # sign.

# This is a class which defines a sample. The sample has two
# lists of double values.
# Use this comment when you need to describe the functionalities and
# feature of the following class or function.

In addition, close to particular commands or expressions, or variable assignments, some brief comments are inserted. Comments are indicative, called inline comments. They should contain only a few characters and be separate from the statement for at least two blank spaces.

&nbsp;x = function(y) # getting a result

These comments can be useful if placed in the right place and reduced to the essentials. Do not exaggerate and do not write the obvious, otherwise the readability of the text will gradually diminishing.

Naming convention

Perhaps the most painful and less respected aspect is precisely the naming convention.

The names of the modules may be all lowercase, or to have initial capital letters. For example, MyModule or mymodule are both used. Generally, the modules that contain only functions have completely lowercase names. However, when you’re dealing with real packages, then the name should be all lowercase.

The names of the classes have capital letters, type MyClass.

The names of the functions can be expressed in any way, or with initial uppercase type MyFunction, or all lower case, type myfunction, or even with the underlines to indicate the spaces, as my_function. As regards the methods, the rules are almost the same. In contrast, for internal methods and instance variables, it is preferable to add an underscore to the name. This avoids any conflict of name that could be generated. While you will need to use two underscores for the attributes and functions that only the current class can access it.

Python Mutable Defaults Are The Source of All Evil

Do not use mutable default arguments in Python, unless you have a REALLY good reason to do so.

Why? Because it can lead to all sorts of nasty and horrible bugs, give you headaches and waste everyone’s time.

Instead, default to None and assign the mutable value inside the function.

Story time

This error caught me a few times in my early developer journey.

  • When I was just starting making real projects with Python (probably 5–6 years ago), I remember stumbling upon a very strange bug. A list would grow bigger than expected when a function was called multiple times, which would strange cause errors.
  • Once in college, we were assigned an algorithm development project and the instructor sent us a program that unit-tested our code. At one point, I was absolutely convinced that my code was correct, yet the tests kept failing.

In the first case, the young-developer-learning-how-to-debug me was getting angry because he had no idea why that list was growing too big, and he spent hours trying to fix this issue.

In the second case, other fellow students also encountered the problem, and the instructor had no clue either, so everyone’s time was wasted.

In both cases, time and effort could have been saved if we had known about this common mistake.

What were we doing wrong?

It turns out that in both cases, an empty list was used as a default argument to a function. Yep, just that. Something like:

def compute_patterns(inputs=[]):
inputs.append('some stuff')
patterns = ['a list based on'] + inputs
return patterns

Try it out yourself: if you run this function multiple times, you’ll get different results!

>>> compute_patterns()
['a list based on', 'some stuff'] # Expected
>>> compute_patterns()
['a list based on', 'some stuff', 'some stuff'] # Woops!

Although it doesn’t look like much, inputs=[] was the naughty boy causing this mess.

The problem

In Python, when passing a mutable value as a default argument in a function, the default argument is mutated anytime that value is mutated.

Here, “mutable value” refers to anything such as a list, a dictionary or even a class instance.

What happened, exactly?

It is not straight-forward to see why the above fact can be a problem.

Here’s what happens in detail using another example:

def append(element, seq=[]):
return seq
>>> append(1)  # seq is assigned to []
[1] # This returns a reference the *same* list as the default for `seq`
>>> append(2) # => `seq` is now given [1] as a default!
[1, 2] # WTFs and headaches start here…

As you can see, Python “retains” the default value and ties it to the function in some way. Since it is mutated inside the function, it is also mutated as a default. In the end, we use a different default every time the function is called!

The solution

Long story short, the solution is simple.

Use None as default and assign the mutable value inside the function.

So instead, do this:

# 👇
def append(element, seq=None):
if seq is None: # 👍
seq = []
return seq
>>> append(1)  # `seq` is assigned to []
>>> append(2) # `seq` is assigned to [] again!
[2] # Yay!

This is actually a very common pattern in Python; I find myself writing if some_var is None: some_var = default_value literally dozens of time in every project.

It’s so common that there is a Python Enhancement Proposal (PEP) currently in the works (PEP 505 — None-aware operators) that would, among other things, allow to simplify the above code and simply write:

def append(element, seq=None):
seq ??= [] # ✨
return seq

Lessons learned

There you go! Hopefully, you’ll never see yourself using mutable defaults in Python code anymore (except you want to mess with everyone’s nerves).

If you see someone else using them, spread the tip and save their lives too.


This short article can be very helpful for those who program in Python. It is often easy to forget these style rules and no wonder if every time you will give out these rules. Perhaps Python is the programming language most widely used in Open Source. So it’s up to us make it more as consistent and readable as possible.