LinkedIn link GitHub link

Python Technical Guidelines and Best Practices

We are excited to introduce our new Python Technical Guidelines.

At Mirai, we are passionate about Python solutions and strive to always keep a keen eye on the best practices in the field. These guidelines are the result of years of applied project experience, refined into actionable insights to help Python developers follow common best practices and write cleaner, more efficient, and maintainable code. We hope this new resource provides you with a clearer pathway to a more robust and Pythonic coding style, and helps you bridge the gap between theoretical knowledge and hands-on expertise.

With our Python Technical Guidelines, we go in-depth on key Python topics, such as:

  • the fundamentals of object-oriented programming;
  • advanced concepts, like testing frameworks (Pytest) and performance optimization.

The idea behind these guidelines is to keep your code productively organized, and to elevate it such that it meets industry standards, providing a consistent and robust foundation for future maintenance and extension.

In the guidelines, we help you create reproducible environments with different tools, like the ever more popular Pixi. We also delve into how to write concise and clear Python code — which enhances code maintenance — and address the usage of:

  • generator expressions
  • comprehension
  • decorators
  • exception handling

These guides covers as well key aspects of object-oriented programming such as:

  • inheritance
  • polymorphism
  • encapsulation

In the test section, we guide you through setting up pytest and structuring your project such that it allows automatic discovery of tests, following best practices and conventions. We also cover the usage of pytest fixtures.

Finally, we showcase the importance of well-documented code through docstrings and comments and touch on code styling in accordance to the PEP 8 guidelines.

These guidelines are developed as a Quarto website, deployed to GitHub Pages and accessible at mirai-solutions.ch/py-techguides.

Below are a few illustrative snippets from our guidelines to give you a taste of the content referenced in the guidelines. For a more comprehensive and in-depth understanding, please refer to the related section in the Python Technical Guidelines.

Stay tuned for updates and new chapters, and let’s see if we can grow some roots here!

Use of Decorators

The ‘Decorators’ section provides a detailed explanation of how to define and use decorators in Python.

Decorators are a specific type of higher-order functions, in that they take a function as input and return a function, allowing one to extend or alter the behavior of the input function.

def my_decorator(function):
    def wrapper():
        # Do something before
        print("Before")
        result = function()
        # Do something after
        print("After")
        return result
    return wrapper

By using the @my_decorator syntax, you can apply the decorator to the greet function:

@my_decorator
def greet():
    print("Hi")

greet()

This way greet is now the decorated version of itself, and the output of the function is:

Before
Hi
After

Defining Generators

As shown in the ‘Generators’ section, generators are functions that create a special generator iterator by defining how to yield one element at a time, allowing to hold a state that can be used to yield the value for the next element based on previous ones.

A powerful aspect of generators is that they allow to separate the logic for generating the next element in a sequence from the logic that makes use of the values. To illustrate this, we define a generator function for the Fibonacci sequence:

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

The yield keyword returns a value and pauses the function’s execution until another value is requested. Values can be retrieved either using next() or via standard for iteration.

fibonacci_sequence = fibonacci(6)
print(next(fibonacci_sequence))
print(next(fibonacci_sequence))
print(next(fibonacci_sequence))
print(next(fibonacci_sequence))
print(next(fibonacci_sequence))
print(next(fibonacci_sequence))
0
1
1
2
3
5
for element in fibonacci(6):
    print(element)
0
1
1
2
3
5

Generators can be easily converted to lists, but also used in comprehensions or directly as iterators in functions like sum().

list(fibonacci(15))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
sum(fibonacci(15))
986
[_ for _ in fibonacci(15) if _ % 2 == 0]
[0, 2, 8, 34, 144]
sum(_ for _ in fibonacci(15) if _ % 2 == 0)
188