Although Python turned 30 years old last year (2021), only in the last few years has it enjoyed the great explosion of adoption, growth, and forward-thinking development that we’ve come to associate with the language. Many features of Python have remained unchanged since its inception, but with every passing year, and every new edition of Python, along come 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 2022—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 be shared 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 they’re 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 note that, while type hints are not enforced at runtime, you can use Pydantic to make that possible. Many widely used Python projects, like FastAPI, use Pydantic extensively.
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 (3.8, 3.9, 3.10) to satisfy different project requirements, Pyenv lets you switch between them either globally 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 — that you get the specific versions you want, and that they work in the combination you ask for. Pipenv does not, however, speak to packaging in any form, so it’s not ideal for projects that 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 the most recent and 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.
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. Some recent additions include:
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. It lets you make control flow decisions based on the contents or structure of objects.
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, not as keyword arguments. The rationales for doing this generally involve improving the clarity and easing 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, and while Unittest isn’t bad as a default, 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 async 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, er, covered for this, and Pytest even comes with a plug-in to work with it.