DEV Community

Cover image for 10 must-know patterns for writing clean code with Python🐍
Alex Omeyer
Alex Omeyer

Posted on • Updated on

10 must-know patterns for writing clean code with Python🐍

Python is one of the most elegant and clean programming languages, yet having a beautiful and clean syntax is not the same as writing clean code.

Developers still need to learn Python best practices and design patterns to write clean code.

What is clean code?🛁✨

This quote from Bjarne Stroustrup, inventor of the C++ programming language clearly explains what clean code means:

“I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations. Clean code does one thing well.”

From the quote we can pick some of the qualities of clean code:

1. Clean code is focused. Each function, class, or module should do one thing and do it well.
2. Clean code is easy to read and reason about. According to Grady Booch, author of Object-Oriented Analysis and Design with Applications: clean code reads like well-written prose.
3. Clean code is easy to debug.
4. Clean code is easy to maintain. That is it can easily be read and enhanced by other developers.
5. Clean code is highly performant.

Image description

Well, a developer is free to write their code however they please because there is no fixed or binding rule to compel him/her to write clean code. However, bad code can lead to technical debt which can have severe consequences on the company. And this, therefore, is the caveat for writing clean code.

In this article, we would look at some design patterns that help us to write clean code in Python. Let’s learn about them in the next section.

Patterns for writing clean code in Python

Naming convention:

Naming conventions is one of the most useful and important aspects of writing clean code. When naming variables, functions, classes, etc, use meaningful names that are intention-revealing. And this means we would favor long descriptive names over short ambiguous names.

Below are some examples:

1. Use long descriptive names that are easy to read. And this will remove the need for writing unnecessary comments as seen below:

# Not recommended
# The au variable is the number of active users
au = 105

# Recommended 
total_active_users = 105
Enter fullscreen mode Exit fullscreen mode

2. Use descriptive intention revealing names. Other developers should be able to figure out what your variable stores from the name. In a nutshell, your code should be easy to read and reason about.

# Not recommended
c = [“UK”, “USA”, “UAE”]

for x in c:
print(x)

# Recommended
cities = [“UK”, “USA”, “UAE”]
    for city in cities:
        print(city)

Enter fullscreen mode Exit fullscreen mode

3. Avoid using ambiguous shorthand. A variable should have a long descriptive name than a short confusing name.

# Not recommended
fn = 'John'
Ln = ‘Doe’
cre_tmstp = 1621535852

# Recommended
first_name = ‘JOhn’
Las_name = ‘Doe’
creation_timestamp = 1621535852
Enter fullscreen mode Exit fullscreen mode

4. Always use the same vocabulary. Be consistent with your naming convention.
Maintaining a consistent naming convention is important to eliminate confusion when other developers work on your code. And this applies to naming variables, files, functions, and even directory structures.

# Not recommended
client_first_name = ‘John’
customer_last_name = ‘Doe;

# Recommended
client_first_name = ‘John’
client_last_name = ‘Doe’

Also, consider this example:
#bad code
def fetch_clients(response, variable):
    # do something
    pass

def fetch_posts(res, var):
    # do something
    pass

# Recommended
def fetch_clients(response, variable):
    # do something
    pass

def fetch_posts(response, variable):
    # do something
    pass
Enter fullscreen mode Exit fullscreen mode

5. Start tracking codebase issues in your editor.

A major component of keeping your python codebase clean is making it easy for engineers to track and see issues in the code itself. Tracking codebase issues in the editor allows engineers to:

Tracking codebase issues in the editor allows engineers to:

  • Get full visibility on technical debt
  • See context for each codebase issue
  • Reduce context switching
  • Solve technical debt continuously

You can use various tools to track your technical debt but the quickest and easiest way to get started is to use the free Stepsize extensions for VSCode, Visual Studio or JetBrains that integrate with Jira, Linear, Asana and other project management tools.

Image description

6. Don’t use magic numbers. Magic numbers are numbers with special, hardcoded semantics that appear in code but do not have any meaning or explanation. Usually, these numbers appear as literals in more than one location in our code.

import random

# Not recommended
def roll_dice():
    return random.randint(0, 4)  # what is 4 supposed to represent?

# Recommended
DICE_SIDES = 4

def roll_dice():
    return random.randint(0, DICE_SIDES)

Enter fullscreen mode Exit fullscreen mode

Functions

7. Be consistent with your function naming convention.
As seen with variables above, stick to a naming convention when naming functions. Using different naming conventions would confuse other developers.

# Not recommended
def get_users(): 
    # do something
    Pass

def fetch_user(id): 
    # do something
    Pass

def get_posts(): 
    # do something
    Pass

def fetch_post(id):
    # do something
    pass

# Recommended
def fetch_users(): 
    # do something
    Pass

def fetch_user(id): 
    # do something
    Pass

def fetch_posts(): 
    # do something
    Pass

def fetch_post(id):
    # do something
    pass
Enter fullscreen mode Exit fullscreen mode

8. Functions should do one thing and do it well. Write short and simple functions that perform a single task. A good rule of thumb to note is that if your function name contains “and” you may need to split it into two functions.

# Not recommended
def fetch_and_display_users():
users = [] # result from some api call

    for user in users:
        print(user)


# Recommended
def fetch_usersl():
    users = [] # result from some api call
        return users

def display_users(users):
for user in users:
        print(user)
Enter fullscreen mode Exit fullscreen mode

9. Do not use flags or Boolean flags. Boolean flags are variables that hold a boolean value — true or false. These flags are passed to a function and are used by the function to determine its behavior.

text = "Python is a simple and elegant programming language."

# Not recommended
def transform_text(text, uppercase):
    if uppercase:
        return text.upper()
    else:
        return text.lower()

uppercase_text = transform_text(text, True)
lowercase_text = transform_text(text, False)


# Recommended
def transform_to_uppercase(text):
    return text.upper()

def transform_to_lowercase(text):
    return text.lower()

uppercase_text = transform_to_uppercase(text)
lowercase_text = transform_to_lowercase(text)
Enter fullscreen mode Exit fullscreen mode

Image description

Classes:

10. Do not add redundant context. This can occur by adding unnecessary variables to variable names when working with classes.

# Not recommended
class Person:
    def __init__(self, person_username, person_email, person_phone, person_address):
        self.person_username = person_username
        self.person_email = person_email
        self.person_phone = person_phone
        self.person_address = person_address

# Recommended
class Person:
    def __init__(self, username, email, phone, address):

        self.username = username
        self.email = email
        self.phone = phone
        self.address = address
Enter fullscreen mode Exit fullscreen mode

In the example above, since we are already inside the Person class, there's no need to add the person_ prefix to every class variable.

Bonus: Modularize your code:

To keep your code organized and maintainable, split your logic into different files or classes called modules. A module in Python is simply a file that ends with the .py extension. And each module should be focused on doing one thing and doing it well.

You can follow object-oriented — OOP principles such as follow basic OOP principles like encapsulation, abstraction, inheritance, and polymorphism.

Conclusion

Writing clean code comes with a lot of advantages: improving your software quality, code maintainability, and eliminating technical debt.

And in this article, you learned about clean code in general and some patterns to write clean code using the Python programming language. However, these patterns can be replicated in other programming languages too.

Lastly, I hope that by reading this article, you have learned enough about clean code and some useful patterns for writing clean code.


This post was written for the Managing technical debt blog by Lawrence Eagles - a full-stack Javascript developer, a Linux lover, a passionate tutor, and a technical writer. Lawrence brings a strong blend of creativity & simplicity. When not coding or writing, he love watching Basketball✌️

Top comments (22)

Collapse
 
jmccabe profile image
John McCabe • Edited

This is a pretty poor example of clear variable names (see Naming Conventions #2 above)

# Recommended
cities = [“UK”, “USA”, “UAE”]
    for city in cities:
        print(city)
Enter fullscreen mode Exit fullscreen mode

The variable name's purpose would be clearer if it were 'countries', not 'cities'!

Collapse
 
svschoenherr profile image
Sven Schönherr • Edited

For loop should also not be indented...

Collapse
 
a5537 profile image
A5537 • Edited

Maybe this indent decided to leave the print statement (where it is destined to be)

for x in c:
print(x)

# Recommended
cities = [“UK”, “USA”, “UAE”]
    for city in cities:
        print(city)
Enter fullscreen mode Exit fullscreen mode

and moved in with that for loop neighbor against all odds.

Seriously, indent really matters in Python. As well as the case (especially for keywords), so Pass instead of pass

# Recommended
def fetch_users(): 
    # do something
    Pass
Enter fullscreen mode Exit fullscreen mode

will trigger a NameError unless there is a variable named Pass.

And speaking of technical debt, these two kinds of bugs (wrong indentation and case incosistency) in poorly written Python code are sneaky enough to go unnoticed since they may not trigger any error messages in some cases.

Thread Thread
 
potasiak profile image
Sebastian Potasiak

Also, the quotation marks in the cities list are not " (\x22) but (\u201C) and (\u201D) unicode characters. It would raise SyntaxError.

Collapse
 
jmccabe profile image
John McCabe

There's always a possibility that's an issue with the page formatter for code. I know Confluence can be a bit funny about that if you paste into a code block.

Collapse
 
philman profile image
Phillip Jones

Whether on purpose or not, it is a clear example of why it's important to give clear names. If this was a bug, I could clearly find it just by looking at the code.

Collapse
 
jmccabe profile image
John McCabe

It's also a relatively good example of why compiled languages are so (much more) useful :-)

Collapse
 
koljaoh profile image
koljaoh

Is using flags really that bad? Especially for large complex functions? Not using them would mean that I need two (large) functions instead of having one simple switch. I would imagine this often leads to problems because stuff is fixed inside one function but not the other.

Collapse
 
potasiak profile image
Sebastian Potasiak

Yeah, there are cases when it should be done and the cases where it should not. The example in the article is an obvious example when it should not be used though.

The issue is with description of the case. It should've say something along the lines of "boolean flags in function arguments should not change the function's core logic, only its side effects".

Collapse
 
bluemont profile image
Bluemont

Agreed. It’s easily avoided in the article’s example, a one-line function. But flags can help us write DRY code for more complex functions.

Collapse
 
mhe931 profile image
mhe931

Thank you so much for your post. It helps a lot.

p.s: dices have 6 sides I think!!

import random

# Not recommended
def roll_dice():
    return random.randint(0, 4)  # what is 4 supposed to represent?

# Recommended
DICE_SIDES = 4

def roll_dice():
    return random.randint(0, DICE_SIDES)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
drystanfuror profile image
Drystan-Furor

Ever played ttrpg’s?
Dices can have
4, 6, 8, 10, 12, 20, and 100 sides.
Polyhedral dice sets. So to me it did make sense.

Collapse
 
dangermanva profile image
Dan

“Dices” is not a word. Dice is already plural, referring to more than one cube. Die is the word to represent one cube.

Btw Good article, the point was well made.

Collapse
 
md2perpe profile image
Per Persson

I consider the name Las_name confusing. Is it supposed to be last_name or does Las mean something else? (In Sweden, LAS is a law about employment.)

Collapse
 
victorsgb profile image
victorsgb

I think it was a typo

Collapse
 
svschoenherr profile image
Sven Schönherr

There are far too many typos for an article about clean code.

Collapse
 
justmedev profile image
justmedev

the fetch and display function is totally fine by itself. It only makes sense to separate them when you need to only display or only fetch the users separately

Collapse
 
albertosesena profile image
Alberto Seseña

I found interesting this post. Some fundamentals but important points are present here. Good job!!!

Collapse
 
humblegorilla profile image
humblegorilla

Cool

Collapse
 
timhuang profile image
Timothy Huang

Keeping codes clean and easy to read is very important not only Python but also other languages. This is a very useful tips for developers to maintain their codes. Thanks for sharing.

Collapse
 
akindeled profile image
Akindeled

Thank you for this piece...

Collapse
 
devsouvik profile image
Souvik Guria

No one said anything about using comprehensions?