Published in:
Uncategorised
Writing clean code in Python
Python is a programming language that offers a high level of flexibility. The counterpart is that developers can easily use different tricks that will lead to heterogeneity in the source code, decreasing its readability and maintainability. As with any programming language, it’s important to define best practices in a team to bring consistency to the source code, avoid bugs, and save time during code reviews.
At Packmind, we recently ran a webinar with our partner Arolla (the replay is in French) on How to write clean code in Python? We share in this post an extract of the discussed practices.
NB 1: Please note that we don’t claim the following practices are always valid, and the “don’t” examples are always bad. Trust yourself 😉
#1 Use Counter to count occurrences
Using the Counter from thecollection
library is more efficient at run time when you want to count different occurrences of elements in a list, tuple, or another hashable iterable:
#2 Use “in” to simplify if statements
The keyword “in” is an elegant, readable and maintainable way to check the presence of a specific element in a sequence :
#3 Put actual before expected in assertions
Assertions will be easier to read in this order:
#4 Use properties when relevant
Sometimes, when we create a class, we will have a field whose value stems from one or multiple other ones. For example, in a class Person, we can have afull_name
field which concatenates the values of first_name
and last_name
.
In such cases, it is important to protect the content of the composite field by defining a property with the annotation @property. Going back to our example with the class Person, this will prevent a user to set the value of the full_name
from outsite by writing person.full_name = ...
.
#5 Use fully qualified, absolute imports
This makes the code more readable and maintainable, so that when it’s time to modify the code, it is easier to figure where each object in the code comes from. Performance-wise, it is basically the same as importing the full module (ex.import foo
) as Python always loads the full module, whether or not we import just an object of that module.
That is to say if when we write from [foo.bar](<http://foo.bar>) import Bar
, Python loads the entirety of the module foo.bar
and then proceeds to pick Bar
#6 Use iterators instead of explicit lists
Avoid creating a new list when it’s not relevant.
#7 Use list comprehensions
A list comprehension is a way of creating a new list by transforming elements from an existing iterable (such as a list, tuple, or dictionary), and we want to, filter some elements, and perform operations on each element.
#8 Prefer using keyword-only arguments
Many times, especially when there is no logical order between the parameters of a function or method, it is recommended to call the function or method by specifying the name of the parameters (ex.make_coffee(with_sugar=True, with_milk=True)
).
It is possible to force the parameters to be named when the function/method is called. We can do that by using the “*” at the beginning of the parameters.
This avoids many possible issues and confusion.
However, it is not something to do all the time but rather when it makes sense.
Instead of:
We’d prefer:
#9 Use ABCMeta for abstract classes
This practice can be relevant if you work with developers who are not expert in Python, but are more familiar with Java or C#. They’ll be more comfortable with the “abstract” concepts for classes and methods.ABCMeta
is a metaclass (a class that creates classes) in Python. It stands for “Abstract Base Class Meta”.
Instead of:
We’d prefer:
#10 Use a main() function
Avoid global variables and, in general, source code outside functions. Instead of:
We’d prefer:
#11 Do not use empty lists as default arguments
This can lead to unexpected and very weird behavior. Instead of:
We’d prefer:
#12 Prefer f-strings to string concatenation
F-strings allow writing sentences in a far more natural (and admittedly less annoying) way than what string concatenation can provide. Do:
Don’t:
#13 Prefer enumerate() to range(len()) when you want keep the index of iterable items
This practice contributes to code readability as well as its performance while still keeping the index around. The performance gain is because enumerate() creates an iterator for the collection, which is more efficient than looping through each item Don’t:
Do: