Python 3.12 is out!

MP 56: New releases are always fun to see.

If you haven’t heard yet, Python 3.12 was officially released yesterday. It’s always interesting to see what features made it into each new release. Even though the core of the language is very stable, Python as a whole is constantly evolving.

In this post I’ll share some highlights of the new release, and some thoughts about when to consider upgrading.

Official release notes

If you want to follow the ongoing development of Python, one place to look for official announcements is the News tab on the python.org home page. The official release announcement is linked from that page, and it provides a good overview of what to look for in the latest version.

The main place experienced Python programmers go to find out about new releases though, is the official release notes page for new versions. For this release, that’s the What’s New in Python 3.12 page in the official docs. (If you’re really trying to stay up to date with how the language is evolving, you can already read through an early draft of the 3.13 release notes.)

If you have any ongoing interest in Python, it’s well worth your time to skim the official release notes each time a new version comes out.

What I’m excited about

In every new release, there are some things that affect my work directly, and others that only affect me indirectly. These are some of the new features in 3.12 that are most likely to impact my work directly:

Expanded support for f-strings

When f-strings were first supported in Python 3.6, there was a subset of Python syntax that could be used inside the curly braces. This was a significant improvement in how dynamic information is included in strings, but there were many situations where you couldn’t use f-strings. Many of those limitations have been lifted.

Improvements to os and pathlib

One of the things I really appreciate about Python is the ease with which we can write code that works across different platforms. Two of the places I notice this most are when I use elements of the os and pathlib modules.

For example the Path.walk() method makes it easier to walk a directory and all its subdirectories. The os module includes three new functions for exploring Windows filesystems: listdrives(), listmounts(), and listvolumes().

Ongoing improvements to error messages

In the early days of computing, error messages were terse for a variety of reasons. Memory was sparse, so it didn’t make sense to store long messages. You also wouldn’t want to spend valuable processor time trying to identify possible causes of errors, and possible resolutions of those errors. It was better to display a short, concise error message and let the programmer sort out the cause and the solution.

Those limitations aren’t nearly as significant anymore. We can easily store slightly longer but much more informative error messages. We have much better tools for inspecting the program environment to find out the reason an error is being raised, and what the most likely solution might be.

I applaud the work that’s gone into recent improvements to Python’s error messages, and I’m happy to see that continuing through each new release. These changes are obviously beneficial to newer programmers, but they make life easier for experienced programmers as well. Python 3.12 brings improvements to error messages related to failed or missing import statements.

Improvements to comprehensions

List, dictionary, and set comprehensions have all gotten faster. Python used to create a new function object for each comprehension that was executed. Comprehensions are now implemented without the need for their own function object, which speeds up execution.

Other improvements to know about

There are a number of improvements that I won’t use directly for the time being, but that I watch with interest because I know they’re improving the ecosystem overall. Many of the libraries I depend on will start to incorporate these new features, and that’s where I’ll most likely notice a long-term impact.

Support for “isolated subinterpreters”

The most talked-about change in 3.12 is the support for a “per-interpreter GIL”. The GIL is the global interpreter lock. When a process needs access to a resource that it might modify, it’s common for that process to lock that resource until it has finished its work. This is good because it prevents other processes from using resources that might change unexpectedly. However, it can slow things down significantly if multiple processes are waiting on the same resource.

Python has one global lock for the whole interpreter. This has made parallel processing work relatively difficult in Python. The GIL touches all aspects of Python’s internals, and people have been working for years on ways to get past having one global interpreter lock. Python 3.12 is the first version that supports sub-interpreters that each have their own lock. I won’t use this myself for the foreseeable future, but I’m certain it will improve the performance of multiple libraries that I use. So hats off and thank you to everyone who is involved in this work.

Low impact monitoring

I’m a huge fan of profilers, and I have some understanding of what goes into the development of a profiler. Profilers need to monitor what’s happening during a program’s execution. An ideal profiler would have no impact on a program’s execution, but that’s not possible—the profiler has to do some work and inspect what’s happening as the program runs.

Python 3.12 includes support for lower-overhead approaches to monitoring programs as they run. This means more performant debuggers, performance profilers, and memory profilers.

When should you upgrade?

This is an interesting question. It really comes down to how comfortable you are with managing Python environments. These days, there are a number of reliable ways to have multiple Python versions installed to your system. If you have multiple interpreters available, you can tell your editor or IDE which version you want to use for any given project. If you’re using virtual environments, you can create each project’s environment with whichever version of Python you want.

If you’re comfortable managing Python environments, go ahead and install Python 3.12 whenever it’s available through your preferred installation method. For example I use pyenv to manage Python versions, and a new release of pyenv was made today with support for Python 3.12. I’ll probably install 3.12 in the next couple days, when I have a chance to test some of my projects on 3.12.

If you’re not comfortable managing Python versions, it’s probably best to wait a little while, perhaps until 3.12.1 is released. The main reason to wait is that many third-party libraries won’t have support in place for 3.12 for a little while yet. If you’re planning to upgrade to 3.12, and you’re not comfortable managing multiple versions of Python on your system, make sure the libraries you depend on support 3.12 before upgrading.

Conclusions

Python is one of the most popular languages today because it achieves a healthy balance between stability and support for new features. If you’re planning to keep using Python indefinitely, it’s well worth your time to skim through the release notes, and try to understand what new features might be most relevant to your work.

Even if you won’t use many of these new features yourself, you’ll almost certainly see them incorporated into some of the libraries you do use on a regular basis. And if any readers happen to be contributors to 3.12, thank you for the time and effort you’ve given!