engineering

December 08, 2020   |   5min read

What’s New in Python 3.9—A Summary of New Features

Intro

The newest Python version — Python 3.9 — was released on October 5th, 2020. In this article, I will go briefly through the new features. For more detailed information, please check the official documentation.

Timezone support

In previous python versions, you had to use third-party libraries like dateutil or pytz to work with timezones. Python 3.9 brings a new standard library called zoneinfo that makes working with timezones more handy.

Standard UTC timestamp:

>>> from datetime import datetime, timezone
>>> datetime.now(tz=timezone.utc)
datetime.datetime(2020, 10, 28, 6, 47, 4, 941275, tzinfo=datetime.timezone.utc)

Zone aware timestamp:

>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo

>>> datetime.now(tz=ZoneInfo("Europe/Warsaw"))
datetime.datetime(2020, 10, 28, 7, 44, 59, 758797, tzinfo=zoneinfo.ZoneInfo(key='Europe/Warsaw'))

Convenient conversion between time zones:

>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo

>>> warsaw_now = datetime.now(tz=ZoneInfo("Europe/Warsaw"))
>>> warsaw_now
datetime.datetime(2020, 10, 28, 7, 44, 59, 758797, tzinfo=zoneinfo.ZoneInfo(key='Europe/Warsaw'))

>>> warsaw_now.astimezone(ZoneInfo("America/New_York"))
datetime.datetime(2020, 10, 28, 2, 44, 59, 758797, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))

Introduced in PEP 615

Dictionaries updates

New union | and in-place union |= operators for built-in dict class were introduced.

union | - dictionary merge complement do {**dict1, **dict2}

Merging two dictionaries with unpacking (**) and union (|) operators:

>>> english = {1: "one", 2: "two", 3: "three"}
>>> spanish = {2: "duo", 3: "Tres", 4: "cuatro"}

>>> {**spanish, **english} # unpacking syntax
{2: 'two', 3: 'three', 4: 'cuatro', 1: 'one'}

>>> spanish | english # union
{2: 'two', 3: 'three', 4: 'cuatro', 1: 'one'}


>>> english
{1: 'one', 2: 'two', 3: 'three'}
>>> spanish
{2: 'duo', 3: 'Tres', 4: 'cuatro'}

in-place union |= - dictionary update complement to dict.update

Updating dictionary with update() method:

>>> english = {1: "one", 2: "two", 3: "three"}
>>> spanish = {2: "duo", 3: "Tres", 4: "cuatro"}’

>>> english.update(spanish)
>>> english
{1: 'one', 2: 'duo', 3: 'Tres', 4: 'cuatro'}

Updating dictionary with in-lace union |= operator:

>>> english = {1: "one", 2: "two", 3: "three"}
>>> spanish = {2: "duo", 3: "Tres", 4: "cuatro"}>>> english |= spanish
>>> english
{1: 'one', 2: 'duo', 3: 'Tres', 4: 'cuatro'}

The significant advantage of using new operators is that they work on dictionary-like types and keep the type after a merge or update:

>>> from collections import defaultdict

>>> english = defaultdict(lambda: "", {1: "one", 2: "two", 3: "three"})
>>> spanish = defaultdict(lambda: "", {2: "duo", 3: "Tres", 4: "cuatro"})

>>> {**spanish, **english} # unpacking syntax
{2: 'two', 3: 'three', 4: 'cuatro', 1: 'one'}


>>> spanish | english # union
defaultdict(<function <lambda> at 0x10925e160>, {2: 'two', 3: 'three', 4: 'cuatro', 1: 'one'})

Introduced in PEP-584

Improved type hinting

Annotated type hints

The type typing.Annotated was introduced to decorate existing types with context-specific metadata.

from typing import Annotated

def force(
 mass: Annotated[float, "kg"],
 acceleration: Annotated[float, "m/s^2"]
) -> Annotated[float, "Newtons"]:
 """Calculate force"""
 return mass * acceleration

Type hinting generics

There is no need to import capitalIzed built-in collection types (e.g. list or dict) from the typing library anymore.

from typing import List

names: List[str]

They can be used directly now:

names: list[str]

Introduced in PEP 593 and PEP 585

New string methods

Removing prefixes or suffixes from the string hasn’t been convenient in Python so far. The method .strip(“python”) treats “python” not as a substring to be removed but as a set of characters to be stripped from the beginning and end of the string.

>>> "there are new methods in python".strip(" python")
'ere are new methods i'

It is not hard to write a function to strip the beginning or the end of the string, but new Python introduces convenient string methods .removesuffix() and “.removeprefix()” to handle this.

>>> "there are a new methods in python".removeprefix("there are ")
'a new methods in python'
>>> "there are a new methods in python".removesuffix(" python")
'there are a new methods in'

Introduced in PEP 616

New math methods

To easily calculate the Greatest Common Divisor (GCD) and the Least Common Multiple (LCM), new methods were introduced in the math library.

The great common divisor is the largest number that divides all numbers passed as arguments.

>>> math.gcd(729, 162)
81
>>> math.gcd(729, 162, 9)
9

The least common multiple is the smallest number that can be divided by all numbers passed as arguments.

>>> math.lcm(18, 81) 
162
>>> math.lcm(18, 81, 162)
162

Other features

Here are some other changes introduced in Python 3.9:

  • a more powerful parser based on PEG (parsing expression grammar) PEP 617
  • new HTTP status codes - 103 (Early Hints) and 425 (Too Early)
  • relaxed grammar restrictions on decorators (PEP 614)
  • optimized signal handling in multithreaded applications.
  • a new graphlib module with the implementation of a topological sort of a graph

For a full list of new features, check the official documentation.

Wrapping up

The newest Python version brings some interesting things to the table. They have been around for some time in beta-versions, so most of the bugs are eliminated. It is definitely worth checking out. However, you have to be aware that some libraries may not support Python 3.9 yet. If you have a Python-based project in mind, don’t hesitate to contact us!

Tobiasz Kędzierski

Software Engineer

Did you enjoy the read?

If you have any questions, don’t hesitate to ask!