Python Best Practices to Follow in 2022

By Saurabh Barot   |   17 January, 2022
Python Best Practices to Follow in 2022

Quick Summary:

Python is one of the most popular programming languages in recent years, with a large active community of programmers and developers worldwide. As one of the most widely used languages, it has several best practices to follow during and after development. This blog will look at some Python Best Practices that you should follow if you want to write cleaner, more professional code.

Python has progressed significantly over the last few decades, and it is now widely used by software developers, data hobbyists, and system administrators. The ease of creating code and community support are two factors that contribute to Python’s appeal.

According to the tiobe-index, python holds the first position. And the graph below shows the popularity of python.

python(TIOBE) Statics

Now that we know how popular Python is let’s look at some of the Python best practices, but first, let’s look at how PEP works. PEP design document that informs the Python community or describes a new feature for Python, its processes, or its environment.

Overview of PEP

As per the official document Python Enhancement Proposal (PEP) is an acronym for Python Enhancement Proposal. A Python Extension Protocol (PEP) is a design document that informs the Python community or describes a new feature for Python, its processes, or its environment. The PEP should provide a technical specification for the feature and the reasoning for it.

Paths of the status of PEPs

PEP allows skilled developers to propose new features to the Python community, raise and track bugs, and detail design considerations for implementing a feature in Python. If you’re new to python programming, I recommend learning how the language and the community around it work to understand the community standards better.

Python Best Practices

Python Best Practices

  • Naming Convention in python
  • Properly Structure your repository
  • Use latest python version(Python 3)
  • Use Ipython
  • Imports
  • One statement of code per line
  • Explicit Code
  • Passing args to Functions
  • Return Statements
  • Writing Idiomatic Python
  • Access a Dictionary Element
  • Updating & Filtering List
  • Read from a file
  • Dealing with Legacy code
  • Use Data classes
  • Merging dictionaries
  • Test Code Frequently
  • Comments
  • Avoid creating a global variable
  • Exception handling
  • Use in-built functions

Python Best Practice #1: Naming Convention in python best practices

When you first start programming in Python or any other programming language, choosing a meaningful class name, variable, function name, and so on takes a lot of time and work. Even though our naming patterns differ from the developer’s, it is frequently recommended that we use a similar naming pattern while writing code. PEP-8 is a community-created stylistic guideline that explains determining and specifying our python program’s naming conventions. Guido van Rossum, Barry Warsaw, and Nick Coghlan created the PEP-8 guidance in 2001 to improve the readability and uniformity of Python code. Below, I’ll go over a couple of the most important naming convention recommended practices from this guideline.

Object Type Naming Example Description
Variables variable,short_variable, this_is_a_long_variable To explain the type of data, you’ll be storing in the variable, use relevant word variables. Long variables should be separated by underscores(_), making them easier to read.
Classes Class, LongClassName When writing the class name in Python, you should utilize the upper-class alphabets for each word.
Methods Class_method(arg_a, arg_b), this_is_a_long_class_method() A method is defined within a class to get or set particular class attributes. When writing method code, it’s best to use all lowercase letters and an underscore to separate the words. Additionally, your method may accept arguments, which you can name using underscores as word separators in the same way method names are named.
Protected Methods _protected_method(self,...) In Python, protected methods are utilized within class objects. These methods are written with an underscore in front of the method name.
Private Methods __ptivate_method(self,...) In Python, private methods are utilized within class objects. These methods have a double underscore in front of the method name.
Functions function(), short_function() A function is a type of function that isn’t linked to a class. For functions and methods, you can use the same naming scheme. Underscores can be used to divide the two.
Constants Constant, THIS_IS_LONG_CONSTANT Constants are values in Python that remain the same throughout the execution flow and are only assigned once during setup. You should use uppercase letters and underscores to separate constants when naming them. You can generate long or short constant names depending on the function of the constant utilized in the context.
Module Module.py,long_module_name.py You can use a Python module to import data into your programs as you build them. It’s simply a Python object that you can use in your code to refer to and bind values to. All lower case characters and underscores should be used as separators in module names.
Package My package, short package A package collects one or more Python files that cooperate to achieve a common purpose. A package can also be a folder with an init.py file and a few Python modules.

Python Best Practice #2: Properly Structure your repository

For python, a few key components should exist within your repository, and you should take time to generate each of these, at least in a basic skeletal form, before actually coding begins.

key components should exist in repository

  • License – [root]: This is the first file you should add to your project. Sites like choosealicense.com might assist you in deciding if you’re unsure.
  • README – [root]: Having a basic README file in place can help you describe your project, clarify its purpose, and outline the basic functionality, whether you choose Markdown, reStructuredText, or even plain text as your format.
  • Module Code – [root] or [/module]: Of course, if you don’t have any new or useful code to write, it’s all for naught. However, make sure to put your real code in a correctly designated sub-directory (e.g.,/module) or, if you’re working on a single-file project, right in the root.
  • py – [root]: In Python projects, a basic setup.py script is fairly popular, as it allows Distutils to build and distribute the modules your project requires properly. More information on setup.py can be found in the official documentation.
  • txt – [root]: While all projects do not utilize a requirements.txt file, it can indicate development modules and other dependencies required to complete the project. For further information, go to the official PIP page.
  • Documentation – [/docs]: Documentation is an important part of every well-structured project, and it’s usually kept under the /docs directory.
  • Tests – [/tests]: Tests, like documentation, are commonly found in most projects and should be kept in their directory.

Overall, this means that a basic Python repository structure would resemble this:



docs/conf.py

docs/index.rst

module/__init__.py

module/core.py

tests/core.py

LICENSE

README.rst

requirements.txt

setup.py

To keep the project setup constant and tidy, use the repository mentioned above to create new projects. Now let’s move on to the next Python best practice, which is similarly about consistency.

Python Best Practice #3: Use Python 3 & Minimum version required

As of January 1, 2020, Python2 is no longer supported. It is therefore recommended to use Python 3 when starting a new project. You can check for the python version of your code to ensure that your users aren’t running an incompatible version of your script. To check, use the following command:

if not sys.version_info > (2, 7):

   # berate your user for running a 10 year

   # python version

elif not sys.version_info >= (3, 5):

# Kindly tell your user (s)he needs to upgrade

   # because you're using 3.5 features

Python Best Practice #4: Use Ipython

IPhython is a more powerful shell. It’s at the heart of the well-known Jupyter Notebook. Just for the autocompletion, it’s worth it. It also contains a built-in collection of useful commands, some of which are shown below:

%cd — to change the current working directory

%edit — to open an editor and execute the code you typed in after closing the editor

%env — to show the current environment variables

%pip install [pkgs] — to install packages without leaving the interactive shell

%time and %timeit — to time the execution of Python code

You can read the full list here.

You can install IPython with:

pip3 install ipython

Python Best Practice #5: Imports Python Best Practice

Imports are always placed at the top of the files, immediately behind module comments and docstrings and before module globals and constants. In most cases, the import should be done on separate lines.

# Correct:

import os

import sys
# Wrong:

import sys, os

It’s okay to say this though

# Correct:

from subprocess import Popen, PIPE

Imports should be grouped in the following order:

Imports in Python

  • Standard library imports.
  • Related Third-party imports.
  • Local application/library specific imports.

You should put a blank line between each group of imports.

Absolute imports are suggested because they are usually more readable and, if the import system is wrongly configured, they tend to function better (or at least offer a better message).

import mypkg. sibling

from mypkg import sibling

from mypkg.sibling import example

However, when working with complex package layouts where absolute imports would be needlessly verbose, explicit relative imports are a valid alternative to absolute imports.

from. import sibling

from .sibling import example

Complex package layouts should be avoided in standard library code, and absolute imports should always be used.

It’s normally fine to spell this when importing a class from a class-containing module:

from myclass import MyClass

from the test. bar.yourclass import YourClass

if this spelling causes local name clashes, then spell them explicitly:

import myclass

import test. bar.yourclass

Wildcard imports should be avoided since they confuse readers and many automated tools by making it unclear which names are present in the namespace. The republishing of an internal interface as part of a public API is a good use case for a wildcard import.

Python Best Practice #6: One Code Per line Python Best Practice

You’re breaking the spirit of Python if you write unconnected statements in a single line. List comprehensions and a few other compound statements are the sole exceptions. These were permitted and accepted because of their brevity and expressiveness.

Bad Practice

print test; print 'bar.'

if x == 1: print 'test'

if <complex comparison> and <other complex comparison>:

# do something

Best Practice

print 'foo'

print 'bar'

if x == 1:

print 'foo'

cond1 = <complex comparison>

cond2 = <other complex comparison>

if cond1 and cond2:

# do something

Python Best Practice #7:Explicit Code Python Best Practice

The simplest and easiest understanding of writing code is always the best.

­Bad Practice

def make_complex(*args):

x, y = args

return dict(**locals())

The above code returns:

The above code returns:

{'args': (1, 2), 'x': 1, 'y': 2}

This is redundant as we needed just x and y, whereas it also returns ‘args.’

Also, if we had:

def make_complex(*args):

x,y = args

z = x+y

return dict(**locals())

In the locals dict, the above method will also yield 'z':3. The ideal practice is to explain exactly what is required and choose the simplest route possible. Remember to make things straightforward and basic.

Another drawback of this awful technique is that if you run the function make complex(1,2,3) with more than two parameters, it will throw a valueError like this:

python code error

Best Practice

def make_complex(x, y):

return {'x': x, 'y': y}

Python Best Practice #8: Passing args to Functions Python Best Practice

There are four ways of passing arguments to function in python. They are as follows:

Types of Arguments in python

  1. Positional Arguments
  2. Keyword Arguments
  3. Arbiratry Argument List
  4. The Arbitrary Keyword Argument Dictionary

8.1. Positional Arguments

The sequence in which they will be defined is the simplest form of argument that is entirely part of the function’s meaning. Developers don’t have to remember that call area(length, breadth) or send msg(message, recipient) require two parameters or their order in call area(length, breadth) or send msg(message, recipient).

Note: In the above example, you can also call functions with keywords like: call_area(length = 90, breadth = 40) or send _msg (recipient = 'Asher', Message = ' Hey !').

8.2. Keyword Arguments

These are also known as kwargs and are frequently used as optional parameters. When a function has more than two or three parameters, it becomes tough to remember its signature. In this scenario, kwargs with a default value come in helpful. For example, a better way to write the send_msg function would be send_message (message, recipient, CC = None, BCC = None). Here, CC & BCC are optional. If the value is not passed for these parameters, it will be returning as “None.”

8.3. Arbitary Argument List

*args constructs can be used to define a function’s business logic if it requires an extensible number of positional arguments. args will be a tuple of all remaining positional arguments inside the function. For example, send_msg(message, *args) can be called with each recipient as an argument: send_msg('Hello', 'Mom','Cooper',' Dean') & the functions scope will have args equal to ('Mom','Cooper','Dean').

8.4. The Arbitrary Keyword Argument Dictionary

The **kwargs construct can be used if your function requires an indeterminate number of named arguments. kwargs will be a dictionary of all the supplied named arguments that haven’t been caught by the function signature’s other keyword arguments in the function body.

Python gives a variable-length non-keyword argument to the function using *args; however, what if we wish to pass a keyword argument? The variable length of keyword arguments can be passed to the function using **kwargs.

For example, for the function below:

def introduction(**data):

print("\nData type of argument:",type(data))

for key, value in data.items():

print("{} is {}".format(key,value))

introduction(Firstname="Ryan", Lastname="Knox", Age=22, Phone=1234567890)

introduction(Firstname="Aurora", Lastname="Knight", Email="aurora_knight@nomail.com", Country="Wakanda", Age=25, Phone=9876543210)

The output of the example will be:

Data type of argument: <class 'dict'>

Firstname is Ryan

Lastname is Knox

Age is 22

Phone is 1234567890

Data type of argument: <class 'dict'>

Firstname is Aurora

Lastname is Knight

Email is aurora_knight@nomail.com

Country is Wakanda

Age is 25

Phone is 9876543210

Caution: Only utilize these strong techniques if the simpler and clearer design can communicate purpose functions.

Your python function will be perfect if you follow the coding style recommended as follows.

  • Easy to read
  • Easy to change

Python Best Practice #9: Return statements

As a function becomes more sophisticated, it is more likely to include many return statements in its body. To maintain a clear aim and a high level of readability, it’s best to avoid returning relevant values at many output locations in the function body. Consider the following example (which is given in the inline comments) of how to avoid adding numerous output points and instead raise exceptions:

Bad Practice

def complex_function(a, b, c):

if not a:

return None

if not b:

return None

# Some complex code trying to compute x from a, b and c

if x:

return x

if not x:

# Some Plan-B computation of x

return x

Best Practice

def complex_function(a, b, c):

if not a or not b or not c:

raise ValueError("The args can't be None")

# Raising an exception is better

# Some complex code trying to compute x from a, b and c

# Resist temptation to return x if succeeded

if not x:

# Some Plan-B computation of x

return x # One exit point for the returned value x will help maintain the code.

Python Best Practice #10: Writing Idiomatic Python

A term that doesn’t make literal sense but makes sense if you understand the culture in which it evolved is an idiom. Programming idioms are the same way; they’re the little things you regularly perform in a specific programming language or paradigm that only make sense to someone familiar with the culture.

Python newbies may not be aware of idiomatic python. Thus we’ve compiled a list of some of the most popular idioms:

10.1 Unpacking

Try using enumerate() to assign names or references to the items of a list while unpacking it:

for index, item in enumerate(some_list):

# do something with index and item

You can use swap variables:

a, b = b, a

Nested unpacking works too:

a, (b, c) = 1, (2, 3)

In Python 3, PEP 3132 has introduced a new method of extended unpacking:

a, *rest = [1, 2, 3]

# a = 1, rest = [2, 3]

a, *middle, c = [1, 2, 3, 4]

# a = 1, middle = [2, 3], c = 4

10.2 Creating throwaway variables

If you need to assign something for example in unpacking but will not need a variable to do so, use __:

filename = 'foobar.txt'

basename, __, ext = filename.rpartition('.')

Note:

Many python style guides propose using a single underscore(_) instead of two underscores (__) for throwaway variables. The problem is that it is frequently used as an alias for the gettext() method, and it is also used at the interactive prompt to store the value of the previous action.

Instead, a double underscore is exactly as apparent and almost as convenient. The advantage of this method is that it eliminates the possibility of inadvertently interfering with either of these use cases.

10.3 Create a length-N list of the same thing

Use the Python list * operator to create simple lists and nested lists as well:

Nones = [none]*4

Fours = [[4]]*5

Output:

[none,none,none,none]

[[4], [4], [4], [4], [4]]

10.4 Search for an item in a collection

We occasionally need to look through a collection. Python offers two types of data structures: lists and sets. Consider the following scenario:

def in_test(iterable):

for i in range(1000):

if i in iterable:

pass

from timeit import timeit

timeit(

"in_test(iterable)",

setup="from __main__ import in_test; iterable = set(range(1000))",

number=10000)

Output: 0.5591847896575928

timeit(

"in_test(iterable)",

setup="from __main__ import in_test; iterable = list(range(1000))",

number=10000)

Output: 50.18339991569519

timeit(

"in_test(iterable)",

setup="from __main__ import in_test; iterable = tuple(range(1000))",

number=10000)

Output: 51.597304821014404

Because lookup set() makes use of the fact that sets in Python are hashtables, the two procedures appear to be identical. However, the lookup performance of the two is different, with the set using O(log n) and the list using O(log n) time complexity (n).

Python will have to go through each item until it finds a matched item to see it in a list. This takes a long time, especially if you have a huge list. The hash of an item in a set, on the other hand, tells Python where to look for a matched item. As a result, the search can be completed rapidly even if the set is big. Because of these variations in performance, it’s always a good idea to utilize sets or dictionaries instead of lists in situations like this:

  • The collection will contain a large number of items
  • You will repeatedly be searching for items in the collection
  • You do not have duplicate items

Python Best Practice #11: Access a Dictionary Elements

Use the dict. has key() method instead. Use x in d syntax instead, or supply a default parameter to dict. get (), which is more pythonic and has been deleted in Python 3.

Bad practice

d = {'Milky': 'bar'}

if d.has_key('Milky'):

print d['Milky']    # prints 'bar'

else:

print 'default_value'

Best Practice

d = {'Milky': 'bar'}

print d.get('Milky', 'default_value') # prints 'bar'

print d.get('thingy', 'default_value') # prints 'default_value'

# alternative

if 'hello' in d:

print d['Milky']

Python Best Practice #12: Filtering List

It is never a good idea to remove items from a list while iterating it. Why? The fact that you’re only reseating one of the references and not changing the list object itself can cause subtle, disastrous issues if your list is accessible via many references.

Bad Practice

# Filter elements greater than 4

num_list = [1, 2, 3]

for i in num_list:

if i > 2:

num_list.remove(i)

There is no need to do multiple passes through the list.

while i in num_list:

num_list.remove(i)

Best Practice

Use a list comprehension or greater expression:

# comprehensions create a new list object

filtered_values = [value for value in sequence if value != x]

# generators don't create another list

filtered_values = (value for value in sequence if value != x) Python Best Practice #1:

Python Best Practice #13: Read From a File

To read from files, use the open syntax. This will close files for you automatically.

Bad Practice

f = open('file.txt')

a = f.read()

print a

f.close()

Best Practice

with open('file.txt') as f:

for line in f:

print line

The approach is preferable since it assures that you always select the correct file, even if an exception occurs within the block.

Python Best Practice #14: Dealing with Legacy Code

When we join a new company, we are frequently handed a codebase to understand and refactor, or we are required to take on legacy code to refactor. You may be in a state of great distress and unable to determine where to begin.

It’s critical to define “legacy code/project” at this point so that we’re everyone on the same page. You’ll come across the following:

  • An older project that has been around forever
  • Codebase without any kind of tests
  • A project that no one wants to work on
  • Everyone that worked on this project left the company years ago

Here’s a quick checklist to help you make your refactoring trip easier and more enjoyable:

  1. Ensure that the project is in a version control system first and foremost. system
  2. Remove the code that has been commented out.
  3. Run/additional tests Ensure that at least 80% of your tests are covered. To track test coverage, use pytest or comparable Python libraries.
  4. Make use of Pylint or Vulture. Always run some sort of linter over your code to see how “healthy” it is. Look for the following:
    • Unused variables
    • Anything that is noted as a potential bug
  5. Make use of formators such as Flake8 or PEP8. These recommendations can be used to reformat Python code such that it complies with PEP8.
  6.  Write more idiomatic Python

Python Best Practice #15: Use data classes

Python has had data classes since version 3.7. There are several advantages to using this method over regular classes or other alternatives such as returning multiple values or dictionaries:

Advantages of data classes In Python

  • A data class requires a minimal amount of code.
  • You can compare data classes because _eq_ is implemented for you.
  • You can easily print a data class for debugging because __repr__ is also implemented.
  • Data classes require type hints, reducing the chances of bugs.

For better understanding, let’s take a look at the example:

from dataclasses import dataclass

@dataclass

class Card:

rank: str

suit: str

card = Card("Q", "hearts")

print(card == card)

# True

print(card.rank)

# 'Q'

print(card)

Card(rank='Q', suit='hearts')

Python Best Practice #16: Merging dictionaries

Since Python 3.5, it becme easier to merge dictionaries:

dict1 = { 'a': 1, 'b': 2 }

dict2 = { 'b': 3, 'c': 4 }

merged = { **dict1, **dict2 }

print (merged)

# {'a': 1, 'b': 3, 'c': 4}

# Python >= 3.9 only

merged = dict1 | dict2

print (merged)

# {'a': 1, 'b': 3, 'c': 4}

If there is an overlap between the keys, the keys from the first dictionary will be overwritten.

Python Best Practice #17: Test Code Frequently

Testing your code frequently is one of the finest python best practices you should follow while writing, and it should come as no surprise. Testing your program regularly makes it easy to detect faults in code and ensures that you are not building on faulty code, leading to further problems down the line. This best practice will aid in detecting errors, similar to the concept of correcting faults as soon as they arise. There are numerous code debugging and software testing tools available. Code refactoring and other software development tools can help you optimize your code and remove any redundant or badly coded portions.

Python Best Practice #18: Comments

Comments that conflict with the code is worse than none at all. When the code changes, make it a point to keep the comments updated. Complete sentences should be used in comments. Unless it is an identifier, the initial word should be capitalized. Identifiers should never be charged in the case.

There are three types of comments in Python

Types of comments in python

  1. Single line comment
  2. Multiline comment
  3. Docstring comment

18.1. Single line comment

These comments start with the hash (#) symbol and last until the end of the line.

#This is a comment

#Printing Hello World!

print("Hello World!")

18.2. Multiline comment

It’s a text that’s surrounded by a delimiter ( ” ” “) at the beginning and conclusion of the comment. There should be no whitespace between the delimiter and the next delimiter ( ” ” ” ). When a comment spans more than one line, these comments are used.

"""

This is a multi-line comment,

spanning over three lines.

This is how it's done.

"""

print("Sum of two numbers:", 45+23)

18.3. Docstring Comments

Python has this built-in feature. It’s used to link documentation written for Python modules, functions, classes, and methods. It is used to indicate what functions, modules, and classes are doing. The _doc attribute makes the docstring comments available.

def add(a, b):

return a+b

print(add.__doc__)

Output

Add the two numbers a and b

Using these comments will help other developers to know what is done in the code.

Python Best Practice #19: Avoid Creating Global Variable

Global variables stay in the application until it is finished running; they exist in the code space and can be called from anywhere at any time. These are convenient because of their accessibility, but they are also a disaster for developers. Because memory can’t be used efficiently because of these variables, a big portion of your memory will be dedicated to these global variables.

There is yet another issue with them. Because any function in your program can access global variables, determine which function is reading or writing the value of those global variables. To figure this out, you’ll have to look at all functions individually, which is a pain. As a result, it’s best to avoid using a global variable.

Python Best Practice #20: Exception Handling

This is an important python best practice that all developers, not only Professional python developers, adopt. So, why is it needed in the first place? Let’s look at an example to help you comprehend it better.

Assume you built a program to open a text file and perform various operations on it, such as reading, closing, and determining the file’s length. This would be a shortcode to create, but what if any errors happened during execution, such as those listed below?

  • What happens if you can’t get the file to open?
  • What if you can’t figure out how long the file is?
  • What happens if the read doesn’t go as planned?
  • What happens if you can’t close the file?

What if the software generated these errors? Your application will not run since your code will come to a halt. As a result, these are possible faults that could occur for various reasons. As a result, it’s critical to manage these exceptions instead of disregarding them. As a result, using exception handling in your code is a must.

try:

print(y)

except:

print("An exception occurred in your code!")

Because y isn’t defined, the try block will throw an exception.

have a unique app Idea?

Hire Certified Developers To Build Robust Feature, Rich App And Websites

Wrapping Up!

While Python provides you with a very powerful development environment, you must take great care in using it. Some Python Best practices are described in this article to help developers become better Python programmers. Although the above list of best practices may not be complete, you must nevertheless put them into practice, as well as many more that you come across. This will assist you in your quest to learn everything Python offers.

Get in Touch!

Saurabh Barot

Saurabh Barot is co-founder and Experienced Chief Technology Officer at Aglowid IT Solutions. His deep industry experience in Angular, React, Ruby on Rails, PHP, Mean Stack and just to name few, has helped the company reach to a new level. He believes that technology is all about exploring new possibilities. Whatever a mind can think (exception can't be ruled out), technology brings it into reality.

Related Posts