First off, I had planned to wait until November 3rd to publish this article, but I couldn’t help it and had to break the news very immediately. On November 24th, Python 3.11 was published, and I have been experimenting with it ever since.
TL; DR – What is new in 3.11
- faster code execution thanks to Faster CPython project
- simplification of working with asynchronous code
- TOML (Tom’s Obvious Minimal Language) can now be used for configuration files
- static typing got improved by new typying features
- we now have better and more decorated error messages with more informative tracebacks
I will be talking about some of these new changes.
Faster code execution
Python has been notorious for being at the slower side of programming languages. For example a loop statement in Python is several times slower than its counterpart in C. This drawback is compensated by the speed of development in Python. Nevertheless, there has been a push for making the code of core Python faster. In late 2020 Mark Shannon has published a proposal on how to speed up CPython by factor of approximately five which he intends on doing over several releases.
This new initiative looks pretty promising as Microsoft has supported the group of developers behind Faster CPython project. Members include some well known names such as Guido van Rossum. There are a lot of changes that have made it into the 3.11 such as bytecode generation “quickening” that takes instructions that can be optimized and replaces it with the adaptive instruction.
Unfortunately I didn’t have enough time to test the bytecode improvements so I am thinking about making it into a separate post another time.
Support for TOML
TOML stands for Tom’s Obvious Minimal Language. Its description also claims that TOML makes configuration files easier for humans to read, and because this claim is accurate, TOML has grown significantly in popularity over the past few years.
For long time, Python hasn’t had built-in support for TOML. This has been changed in Python 3.11 and tomllib is now part of the standard library and allows for parsing of TOML files.
We can demonstrate new TOML integration using this example .toml file I have created.
# TOML document
title = "Hung's TOML Example"
[owner]
name = "Hung"
loves = [["pancakes", "videogames", "cats"]]
[servers.alpha]
ip = "10.0.0.38"
role = "frontend"
[servers.beta]
ip = "10.0.0.39"
role = "backend"
Which I will parse using this Python code that imports tomllib and opens a given file as readable binary file. Contents of the opened file are then loaded to the configuration table that is by default a dictionary.
import tomllib
with open("hung_example.toml", "rb") as f:
configuration = tomllib.load(f)
After running the script the contents of configuration variable will be:
{'title': "Hung's TOML Example",
'owner': {'name': 'Hung', 'loves': [['pancakes', 'videogames', 'cats']]},
'servers': {'alpha': {'ip': '10.0.0.38', 'role': 'frontend'}, 'beta': {'ip':
'10.0.0.39', 'role': 'backend'}}}
As you can see, parameters such as name and loves are nested within the set. I personally like TOML more than using YAML as I find it much easier to read and understand. And that’s why bringing tomllib as a part of a standard library really warms my cockles.
Static typing got buffed
Hands on heart, who doesn’t do static typing these days? The importance of static typing is beyond any doubts. Defining the exact type of variables, function parameters and outputs can save you a lot of debugging time. Pepping up your code with typing annotations will also cause modern IDEs to display function definitions as you type their names making it easier to interpret by other users.
The holy and powerful typing module in Python has classes for almost all the data types except when the class returns its instance. Python 3.11 introduces the new Self class that can be added as a function’s return value (in Python 3.10 and previous we had to use TypeVar and bind it to the respective class).
Imagine having a Counter class that has methods for increasing and decreasing the counter value. Both of these methods return instance of the same class (same counter) after performing its operation. In this situation, using Self annotation may be helpful.
from typing import Self
class Counter:
def __init__(self, start_value: int = 0) -> None:
self.value = start_value
def increase(self) -> Self:
self.value += 1
return self
def decrease(self) -> Self:
self.value -= 1
return self
Error messages got even prettier
Let’s remove the importing of Self from the typing module and modify the previous code in this manner.
class Counter:
def increase(self) -> Self:
self.value += 1
return self
New changes introduced within Python 3.11 makes the traceback and error messages more informative by specifying the exact piece of code that is causing the troubles.
If you have read so far, you might want to follow me here on Hashnode. Feel free to connect with me over at LinkedIn or Mastodon.