Clean Code in Python

Clean Code in Python

Attract fewer bugs with the right tools

This comprehensive guide will delve into powerful tools and techniques that will make your Python code squeaky clean and attract fewer bugs. From Python typing to Pylint, Black, venv, essential files, and folder structure, we'll cover it all. Whether you're a seasoned Python developer or a curious learner, this post is your key to writing clean and elegant Python code.

Typing

Python is a dynamically typed language, which means that the type of a variable is determined at runtime based on the value assigned to it. This is different from statically typed languages, where variable types are explicitly declared during compilation. Dynamic typing in Python makes life easier as developers don't need to specify data types, leading to more flexible and concise code. However, this flexibility comes with a downside. Since Python doesn't enforce strict type-checking, it leaves room for unexpected behaviour and can lead to more bugs in the code.

To address this, Python introduced "typing," a feature that allows developers to assign types to variables, function parameters, and return values. By using type hints, we can add clarity to our code and catch type-related errors early on during development. Here's an example:

def add_numbers(a: int, b: int) -> int:
    return a + b

result = add_numbers(5, 10)
print(result) # Output: 15

result = add_numbers("5", "10") # This will raise a TypeError

In this example, we've used type hints to indicate that the parameters "a" and "b" should be integers, and the function should return an integer. When the function is called with proper integer arguments, it works perfectly fine. However, if we try to pass strings as arguments, Python will raise a TypeError, helping us catch the bug early and ensuring more robust code.

For an extensive guide on Python typing, check out this comprehensive guide from Real Python

Linting

Linters are powerful tools that analyze your code and provide suggestions to ensure it follows coding standards and best practices. They are essential for writing clean, maintainable code and catching potential bugs early in the development process. In Python, one popular linter is Pylint.

Pylint checks your code against a set of predefined coding conventions and rates your code's quality with a score. A score of 10.0 indicates perfect code adherence, while anything below may need improvements. Pylint not only helps you improve code readability and consistency but also encourages you to write more Pythonic code.

To use Pylint in your Python project, you can simply install it using pip and run it on your project folder or specific files. For example:

pip install pylint
pylint your_project_folder

You can also configure Pylint settings for your project using the pyproject.toml file, which will be introduced later on in this guide.

Check out the Pylint official user manual to get started.

Formatting

Code formatters are essential tools in the quest for clean and consistent code. They help automatically format your code to adhere to a predefined style, eliminating the need for manual adjustments.

One popular code formatter for Python is Black. It's known for its opinionated approach to formatting, ensuring that all Python code is formatted in the same consistent style.

To install Black, simply open your terminal and execute the following command:

pip install black

After installing Black, you can also configure it in your pyproject.toml file to suit your project's specific needs.

Refer to the official guide to introduce Black to your project.

Now, with Black and Pylint by your side, you're well-equipped to maintain clean, organized, and readable Python code throughout your project.

Venv

Virtual environments (venv) are a lifesaver in Python development, especially when working in teams or handling multiple projects. They provide an isolated and self-contained environment for your Python project, ensuring that its dependencies don't interfere with other projects or the system-wide Python installation. This isolation is particularly beneficial when different projects require different package versions.

One of the advantages of venv is its compatibility with Docker, which makes it easier to manage dependencies and reproduce the same environment across different machines. Moreover, setting up a venv is incredibly fast and straightforward compared to system-wide installations.

To create a virtual environment and install project dependencies, follow these steps:

  1. Open your terminal and navigate to your project directory.

  2. Create a new venv by running:

python -m venv venv
  1. Activate the venv:
  • On Windows:
venv\Scripts\activate
  • On macOS/Linux:
source venv/bin/activate
  1. Once the venv is activated, you'll notice the terminal prompt changes, indicating that you're working within the virtual environment.

  2. Now, you can install your project's dependencies using the requirements.txt file:

pip install -r requirements.txt

With the virtual environment set up, you can confidently work on your project without worrying about conflicting dependencies. It's an excellent practice to always use virtual environments, and it will save you and your team from potential headaches in the future.

This guide on Python virtual environments will provide you with everything you need to start using venv in your Python projects.

Essential Files

In the previous sections of this guide, we briefly mentioned the pyproject.toml file and the requirements.txt and requirements.dev.txt files, but now it's time to dive into each of them and understand their importance and uses in detail.

Pyproject

The pyproject.toml file is a configuration file commonly used for Python projects. It allows you to define various project settings, including dependencies, build requirements, and tools configuration. One of the key benefits of using pyproject.toml is that it provides a standardized way to manage project metadata and settings, making it easier for other developers to understand and collaborate on your project.

Here's an example of a pyproject.toml file with configuration settings for metadata, Pylint, and Black:

[tool.poetry]
name = "my-python-project"
version = "0.1.0"
description = "A simple Python project"
authors = ["Your Name <your.email@example.com>"]

[tool.pylint]
# Pylint configuration options go here
# For example, to ignore specific warnings:
disable = "missing-docstring,invalid-name"

[tool.black]
# Black configuration options go here
# For example, to use single quotes and enable trailing commas:
line-length = 79
single-quotes = true
include-trailing-comma = true

# Starting point file
init-hook = "import sys; sys.path.append('./src'); "

In this example, we have set up the project metadata in the [tool.poetry] section. Here, you can specify the project name, version, description, and author information.

For Pylint, we have added a [tool.pylint] section where you can configure various Pylint options. In this example, we have used the disable option to ignore specific Pylint warnings. For instance, we disabled the "missing-docstring" and "invalid-name" warnings.

Similarly, for Black, we have included a [tool.black] section to configure its options. In this example, we have set the line-length to 79 characters, which is the maximum line length that Black will allow. We also enabled the single-quotes option to use single quotes for strings, and the include-trailing-comma option to add trailing commas in lists and tuples.

Lastly, we have the init-hook line, which appends the ./src directory to the Python path. This is useful when you have a specific starting point file (e.g., main.py) in the ./src directory, and it helps Python find and run the code within that directory.

With this pyproject.toml file, you can easily manage your project's metadata and configure Pylint and Black according to your preferred settings.

Requirements

The requirements.txt and requirements.dev.txt files are used to manage project dependencies. The requirements.txt file contains the main dependencies needed to run your project, while the requirements.dev.txt file includes additional development dependencies, such as testing frameworks, linters, and code formatters.

By keeping your dependencies in separate files, you can clearly distinguish between the dependencies needed for production and those required for development. This separation helps in creating a clean and organized development environment and makes it easier to share your project with others.

To use these files effectively, you can create a pyproject.toml file and define your project settings and dependencies in it. Then, you can use the pip command to install the dependencies from the requirements.txt and requirements.dev.txt files:

pip install -r requirements.txt
pip install -r requirements.dev.txt

By leveraging these files, you can ensure consistent and reproducible environments for your Python projects, making collaboration and deployment much smoother.

This tutorial will get you started with the requirements.txt file.

Project Structure

Structuring your Python project with clean folders is essential for maintaining a maintainable and scalable codebase. In this section, we'll explore the best practices for organizing your project with an example of a folder directory tree that starts with a "src" folder, containing "modules," "config," and "test" subfolders.

The "src" folder serves as the heart of your project and holds all the source code for your application. Inside the "src" folder, you can create subfolders to logically group different parts of your codebase. For example, the "modules" folder can house different Python modules or packages that define specific functionalities or features of your application. The "config" folder can contain configuration files or settings for your project, making it easier to manage various configurations in one place.

The "test" folder is crucial for writing unit tests and ensuring the reliability of your code. By keeping test files separate from the actual source code, you create a clear distinction between production code and test code, making it easier to maintain and manage your project.

Now, let's take a look at the example folder directory tree:

my_project/
└── src/
    ├── modules/
    │   ├── __init__.py
    │   ├── utils.py
    │   └── features.py
    ├── config/
    │   ├── __init__.py
    │   └── settings.py
    └── test/
        ├── __init__.py
        ├── test_utils.py
        └── test_features.py

In this example, the "src" folder is the root of our project, and it contains the "modules," "config," and "test" folders. Inside the "modules" folder, we have two Python files: "utils.py" and "features.py," where each file can define different functionalities. The "config" folder contains a "settings.py" file, where we can store various project configurations.

In the "test" folder, we have test files for our application's modules. For example, "test_utils.py" contains test cases for the functions defined in "utils.py," and "test_features.py" contains test cases for the functions defined in "features.py." Separating test files from the main source code ensures that testing is an integral part of the development process and helps maintain code quality and reliability.

By structuring your Python project with clean folders and a clear organization, you'll create a more maintainable and scalable codebase, making collaboration with other developers smoother and improving the overall development experience.


Conclusion

Congratulations! You've now mastered the art of writing clean and elegant Python code. By following the techniques and tools discussed in this guide, you can attract fewer bugs, improve code readability, and maintain a more organized and efficient development workflow.

We started by exploring the power of Python typing, which allows you to add type hints to variables and functions, making your code more robust and catching potential errors early on. We then dived into the world of linting, where Pylint became your best friend in maintaining coding standards and enhancing code quality.

Next, we introduced you to the magical code formatter, Black, which automatically formats your Python code, ensuring consistent and clean style. With Black and Pylint, you can rest assured that your code will always look polished and professional.

We also learned the importance of virtual environments (venv) for managing project dependencies and isolating your Python projects. The speed and simplicity of setting up venv make it a must-have tool for Python development, especially when working in teams or handling multiple projects.

Additionally, we discussed essential files like pyproject.toml, requirements.txt, and requirements.dev.txt, which provide standardized ways to manage project metadata and dependencies, making it easier for other developers to understand and collaborate.

Lastly, we explored the significance of structuring your Python project with clean folders, ensuring a more organized and scalable codebase. With the example folder directory tree, you have a clear blueprint to structure your projects for better readability and maintainability.

Remember, writing clean code is not just a one-time effort but an ongoing journey. As you continue to apply these practices and tools, your Python projects will become even more polished and efficient.

So, whether you're a seasoned Python developer or just starting your Python journey, keep writing clean code and striving for excellence in every line of your code!