Python 3.9 - What's new

14. January, 2021 5 min read Python

What’s new in Python 3.9.x

Python 3.9.0 has been released on the 5th of October, and Python 3.9.1 followed shortly after on the 7th of December.

They introduce a bunch of new functionality to Python’s syntax, build-in features, the standard library and the interpreter. For my learning objective, I’d like to go over some fundamental changes and provide examples and tips on using some of the new features.

Let’s start with how the language is maintained first, before moving onwards to the fancy part.

Moving towards a yearly release cycle

Previously, Python has been released on an eighteen-month cycle. 3.9 will change this to an annual release cycle. This change comes from the increasing popularity of the language in various areas such as data science or machine learning..

Naturally, releases will become smaller to land features and bug fixes sooner into the users’ hands. A shorter release cycle also decreases the rush before betas and creates further predictable planning for everyone involved.

Related Python Enhancement Proposals:

Performance improvements

Python has continuously improved its performance on every past release. However, 3.9 introduced two changes that don’t require any code adaptions at all. Without getting into details, the new PEG parser offers more efficiently, and many builtins now use the vectorcall convention to make their execution faster.

Related Python Enhancement Proposals:

  • PEP 590 – Vectorcall: a fast calling protocol for CPython
  • PEP 617 – New PEG parser for CPython

Internal changes

Cleaning up code, including improving and modernizing the codebase, is an ongoing development task for every developer. This approach applies to Python’s core developers as well. Besides the removal of deprecated compatibility code, there are some noteworthy internal changes.

These include; module state access is now possible from C extension methods, garbage collection doesn’t block on resurrected objects, and several additional modules use multi-phase initialization and/or the stable ABI.

Related Python Enhancement Proposals:

  • PEP 384 – Defining a Stable ABI
  • PEP 489 – Multi-phase extension module initialization
  • PEP 573 – Module State Access from C Extension Methods

New features

There are three significant features I’d like to dig deeper into:

  • how prefixes and suffixes work in string operations
  • what the union operator allows us to do in dictionaries
  • the relaxation on decorators to improve maintainability

However, all changes are listed after them for completeness.

Prefixes and suffixes string operations

PEP 616 introduces string methods to remove prefixes and suffixes. Let’s have a look at an example:

if test_func_name.startswith("test_"):
    print(test_func_name[5:])
else:
    print(test_func_name)

There we check if test_func_name starts with “test_” (prefix). If this matches, we remove “test_” and return the new value or return test_func_name.

This can now be simplified by using removeprefix or removesuffix respectively:

# for the prefix case
print(test_func_name.removeprefix("test_"))

# for the suffix case
print(test_func_name.removesuffix("test_"))

Add union operators to dictionaries

PEP 584 introduces a union operator to dictionaries. That operator dramatically simplifies how two dictionaries can be merged. Let’s have a look at an example:

d1 = { "spam": 1 }
d2 = { "eggs": 3 }

print(dict(**d1, **d2))

You may also want to have a look at dict.update or collections.ChainMap, as these offer similar functionalities. Though the union operator is more readable and avoids some pitfalls when merging objects:

d1 = { "spam": 1 }
d2 = { "eggs": 3 }

print(d1 | d2)

More flexible decorators

PEP 614 relaxes the grammar restrictions on decorators. They were initially limited as a preference rather than a technical requirement. To demonstrate the benefits, let’s have a look at a code example:

button_0 = buttons[0]
button_1 = buttons[1]

@button_0.clicked.connect
def spam():
    ...

@button_1.clicked.connect
def eggs():
    ...

You can now simplify and make it more readable, idiomatic and maintainable:

buttons = [button_0, button_1]

@buttons[0].clicked.connect
def spam():
    ...

@buttons[1].clicked.connect
def eggs():
    ...

More type hinting

PEP 585 adds more type hinting capabilities which were first introduced in Python 3.5. More specifically, Python now enables support for the generics syntax in all standard collections currently available in the typing module.

PEP 593 adds a mechanism to extend the type annotations from PEP 484 with arbitrary metadata.

IANA time zone support

PEP 615 introduces support for the IANA time zone database in the standard library. This feature is a bit more abstract (and I don’t understand it well enough), so I recommend to dig into the PEP specification to learn more about it.

You can find more information about the IANA time zone database on their website.

Other changes

  • os.pidfd_open() has been added that allows process management without races and signals.
  • An implementation of a topological sort of a graph is now provided in the new graphlib module.

Summary

Python 3.9 adds a lot of excellent new features to improve the quality of life for any developer. A faster release cycle will allow us to upgrade faster with fewer compatibility issues and the new features are a welcome addition to the language.

We expect Python 3.10 to be released around the 4th of October. But don’t worry, all releases are supported for five years, so 3.9 will be active until 2025.

‘Till next time!