4 keys to writing modern Python


Although Python had its 30-year anniversary in 2021, the explosion of adoption, growth, and forward-thinking development associated with the language is still relatively new. Many features of Python have remained unchanged since its inception, but with every passing year, and every new edition of Python, there are new ways of doing things and new libraries that take advantage of those advances.

So, Python has its old ways and its new ways. Naturally, it makes sense to learn how to work with Python using its most modern and convenient features. Here, we’ll run down the key concepts you need to understand to write modern Python in 2024—software that uses Python’s latest and greatest idioms, concepts, and capabilities.

Type hinting in Python

Python’s recently introduced type hinting syntax allows linters and third-party code quality tools to analyze your code before runtime, and to detect possible errors before they buzz out. The more you create Python code to share with others, the more likely you (and everyone else!) will benefit from using type hints.

Each successive revision of Python rolls out more sophisticated and powerful type annotations. If you get into the habit of learning how to use type annotations in the short run, you will be better equipped to make use of each new type hinting innovation as it’s introduced.

It’s important to remember that type hints are optional, not mandatory. Not every project needs them. Use type hints to make your bigger projects comprehensible, but feel free to omit them from a 50-line throwaway script. And, while type hints are not enforced at runtime, you can use Pydantic to make that possible. Many widely used Python projects use Pydantic extensively—FastAPI is one example.

Python virtual environments and package management

For simple projects and undemanding development jobs, you can often just use Python’s built-in venv tool to keep projects and their requirements separate. But recent advances in Python’s tooling give you more options:

  • Pyenv: If you need to keep multiple versions of Python installed to satisfy different project requirements, Pyenv lets you switch between them either globally or on a per-project basis. It’s useful if you find yourself doing a lot of work with different Python editions right at the command line, outside of the context of a per-project virtual environment. Note that there is no official Windows support, but an unofficial Windows port does exist.
  • Pipenv: Billed as “Python dev workflow for humans,” Pipenv is meant to manage a virtual environment plus all the dependencies for your project. It also ensures dependencies are deterministic, meaning you get the specific versions you want, and that they work in the combination you request. Pipenv does not, however, speak to packaging in any form, so it’s not ideal for projects you eventually want to upload to PyPI or share with others.
  • Poetry: Expanding on Pipenv’s toolset, Poetry not only manages projects and requirements, but also makes it easy to deploy the project to PyPI. It also manages virtual environments for you separate from your project directories.
  • PDM: PDM (short for Python Development Master) is a recent cutting-edge project in this vein. Like Poetry and Pipenv, PDM provides you with a single interface for setting up a project, managing its dependencies, and building distribution artifacts from it. PDM also uses the PEP 582 standard for storing packages locally to a project, so there is no need to create per-project virtual environments. But this tool is relatively new, so make sure it works provisionally before adopting it in production.
  • Hatch: The hatch project not only handles project setup and management, but also provides a build system, tools for packaging projects for redistribution on PyPI, test handling, and many other useful functions.
  • uv: The experimental uv project is written by the same folks who make the ruff Python linting tool. It aims to replace pip, venv, and several other command-line Python tools at once. It’s written in Rust for speed (like ruff), and many of its commands resemble those of pip and other tools it replaces, making it relatively easy to learn.

When creating new projects that are meant to be worked on in a team environment or distributed to others (e.g., via PyPI), be sure to use the modern pyproject.toml format for your requirements and project configuration, along with the project layout used with it. You can still use the older requirements.txt file side-by-side with pyproject.toml, but the latter covers a wider range of use cases and makes your projects forward-compatible.

New Python syntax

Python’s evolution has meant many new additions to the language itself. The last few versions of Python have added useful syntactical constructions that allow for more powerful and succinct progamming. While they aren’t mandatory, newer third-pary modules may use them, so they’re worth getting to know at least casually.

Three recent syntax additions are especially notable.

Pattern matching

The biggest recent addition, structural pattern matching, which arrived in Python 3.10, is more than just “switch/case for Python” as it has sometimes been described. Structural pattern matching lets you make control-flow decisions based on the contents or structure of objects. In short, it’s a way to match based on types or the shapes of types (a list with an int and a string, for instance) rather than values.

The ‘walrus operator’

So named for its appearance (:=), the walrus operator, added in Python 3.8, introduces assignment expressions, a way to assign a value to a variable and then apply a test to the variable in a single step. It makes for less verbose code in many common situations, such as checking a function’s return value while also preserving the results.

Positional-only parameters

A minor but useful recent addition to Python’s syntax, positional-only parameters, lets you indicate which function parameters must be specified as positional ones, never as keyword arguments. This feature is generally intended to improve the clarity and ease the future development of a codebase, goals that many of Python’s other new features also focus on.

Python testing

Writing tests for a codebase is like flossing daily: Everyone agrees it’s a good thing, few of us actually do it, and even fewer do it properly. Modern Python codebases deserve to have test suites, and the current tooling for testing makes creating test suites easier than ever.

Python has its own built-in testing framework, unittest. It isn’t bad as a default, but its design and behaviors are dated. The Pytest framework has risen to prominence as a common substitute. It’s more flexible (you can declare tests in any part of your code, not just a subset) and requires writing far less boilerplate. Plus, Pytest has plenty of add-ons to expand its functionality (e.g., for testing asynchronous code).

Another important adjunct to testing is code coverage, determining how much of one’s codebase the tests actually cover. The module Coverage has you covered for this (as the name suggests) and Pytest even comes with a plug-in to work with it.

Copyright © 2024 IDG Communications, Inc.



Source link