I’ve been learning about the new features that each new Python release brings that are helpful to developers. Today we’ll start with Python 3.9, which is very middle-of-the-road and doesn’t have many valuable changes.

Add union operators to dict

In PEP 584 - Add Union Operators To dict it is proposed to add 2 operators to dict, | and |=.

There are two common ways to merge 2 dictionaries in the past:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
In : a = {'a': 1}

In : b = {'b': 1}

In : {**a, **b}  # Option 1, using **, Python 3.5 and above
Out: {'a': 1, 'b': 1}

In : c = a.copy() # Option 2, using update

In : c.update(b)  # If you use a.update directly, it will modify the result of a directly

Of course, there are some more tricky ones, notably dict(list(x.items()) + list(y.items()) in Python 2, but I won’t mention them.

Python 3.9 onwards can be implemented using the or operator.

1
2
3
4
5
6
In : a = {'a': 1}

In : b = {'b': 1}

In : a | b
Out: {'a': 1, 'b': 1}

If you want to replace directly after merging, you can use the update (|=) operator.

1
2
3
4
5
6
7
In : a
Out: {'a': 1}

In : a |= b  # Equivalent to a.update(b)

In : a
Out: {'a': 1, 'b': 1}  # Direct replacement on a (in place)

Added string methods to remove prefixes and suffixes

New removeprefix and removesuffix methods have been added to PEP 616. In the past, you had to implement them yourself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def removeprefix(self: str, prefix: str, /) -> str:
    if self.startswith(prefix):
        return self[len(prefix):]
    else:
        return self[:]

def removesuffix(self: str, suffix: str, /) -> str:
    if suffix and self.endswith(suffix):
        return self[:-len(suffix)]
    else:
        return self[:]

Of course, the corresponding methods for bytes, bytearray and collections.UserString have been added as well.

My understanding of this feature is that it can be used to make some improvements to the code in CPython.

Decorator syntax improvements

In previous versions, a decorator is an object that needs to be determined, it does not support some scenarios where expressions are computed, and it does not support anonymous functions as decorators, for 2 examples.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
In : from dataclasses import dataclass
...: d = [dataclass]
...:
...: @d[0]
...: class A:
...:     attr: int
...:
...: print(A)
...:
  File "<ipython-input-1-cf77b251b3d6>", line 4
    @d[0]
      ^
SyntaxError: invalid syntax

In : @lambda func: lambda *args: func(*args)+1
...: def plus(a, b):
...:     return a + b
    File "<ipython-input-3-236754e0ed07>", line 1
    @lambda func: lambda *args: func(*args)+1
     ^
SyntaxError: invalid syntax

This will throw a syntax error straight away, and they all work fine now.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
In : from dataclasses import dataclass
...: d = [dataclass]
...:
...: @d[0]
...: class A:
...:     attr: int
...:

In : print(A)
<class '__main__.A'>

In : @lambda func: lambda *args: func(*args)+1
...: def plus(a, b):
...:     return a + b
...:

In : plus(1, 2)
Out: 4

Summary

There are really no new features that I’m excited about in this release.