What's New Expected in Python 3.9

The new version is expected only in October, but you can already read what awaits us in it and test the preliminary release.
In this article, the most interesting, in my opinion, changes.

Firstly, we are reminded that layers that support backward compatibility with version 2.7 are slowly being removed and asked to pay attention to DeprecationWarning and eliminate them. A few warnings will still remain at 3.9, but it is better to get rid of them.

Dictionary
Merge Operator ( PEP-584 ) Until now, there were several ways to merge dictionaries, but each of them had minor flaws or nuances.
Several ways to combine dictionaries
1. update
d1 = {'one': 1}
d2 = {'two': 2}
#     ,..
d1.update(d2)
# ...  . 
united_dict = d1.copy()
united_dict.update(d2)

2.
united_dict = {**d1, **d2}

, , , .
3. dict(d1, **d2), . , d2 .
4. collections.ChainMap
.
, , .
from collections import ChainMap
d1 = {'one': 1}
d2 = {'two': 2}
united_dict = ChainMap(d1, d2)

, , , . , - .

Now you can write simply
united_dict = d1 | d2
# ,        ,    update():
d1 |= d2

Thus, a new possibility of combining dictionaries with one operator, I think, will appeal to many.

Simplification of annotations for containers and other types that can be parameterized ( PEP-0585 )
The following innovation is very useful for those who use type annotation.
Now simplifies the annotation of collections, such as list and dict, and generally parameterized types.

For these types, the term Generic is introduced - this is a type that can be parameterized, usually a container. For example, dict. And the question is how to correctly translate it so that it does not break teeth. I really do not want to use the word "generic". So in the comments I really look forward to other suggestions. Maybe somewhere in the translations there was a better name?
Parameterized generic: dict [str, int].

So for such types, now you don’t need to import the corresponding annotations from typing, but you can use just the type names.
for instance
# 
from typing import List
List[str]
# 
list[int]

.

:
# 
from typing import OrderedDict
OrderedDict[str, int]
# 
from collections import OrderedDict
OrderedDict[str, int]

tuple # typing.Tuple
list # typing.List
dict # typing.Dict
set # typing.Set
frozenset # typing.FrozenSet
type # typing.Type
collections.deque
collections.defaultdict
collections.OrderedDict
collections.Counter
collections.ChainMap
collections.abc.Awaitable
collections.abc.Coroutine
collections.abc.AsyncIterable
collections.abc.AsyncIterator
collections.abc.AsyncGenerator
collections.abc.Iterable
collections.abc.Iterator
collections.abc.Generator
collections.abc.Reversible
collections.abc.Container
collections.abc.Collection
collections.abc.Callable
collections.abc.Set # typing.AbstractSet
collections.abc.MutableSet
collections.abc.Mapping
collections.abc.MutableMapping
collections.abc.Sequence
collections.abc.MutableSequence
collections.abc.ByteString
collections.abc.MappingView
collections.abc.KeysView
collections.abc.ItemsView
collections.abc.ValuesView
contextlib.AbstractContextManager # typing.ContextManager
contextlib.AbstractAsyncContextManager # typing.AsyncContextManager
re.Pattern # typing.Pattern, typing.re.Pattern
re.Match # typing.Match, typing.re.Match


For strings, removeprefix () and removesuffix () ( PEP 616 ) methods appeared
. Everything is simple here. If the line begins with a prefix, then the line without this prefix will be returned. If the prefix is ​​repeated several times, then it will be deleted only once. Similarly with the suffix:
some_str = 'prefix of some string and here suffix'
some_str.removeprefix('prefix')
>> ' of some string and here suffix'
some_str.removesuffix('suffix')
>> 'prefix of some string and here '

Current alternatives
1. , , — removeprefix.
def removeprefix(self: str, prefix: str, /) -> str:
    if self.startswith(prefix):
        return self[len(prefix):]
    else:
        return self[:]

2. lstrip, rstrip:
	'foobar'.lstrip(('foo',))

, , .

A few changes to the math module
The math.gcd () function to find the largest common divisor now accepts a list of integers, so you can find a common divisor with more than one number with one function.
There is a function for determining the smallest common multiple of math.lcm () , which also accepts an unlimited number of integers.
The following two functions are interconnected.
math.nextafter (x, y) - calculates the floating-point number closest to x if moving in the y direction.
math.ulp (x)- stands for “Unit in the Last Place” and depends on the accuracy of your computer’s calculations. For positive numbers, the lowest value of the number will be returned, such that when it is added x + ulp (x), the nearest floating-point number will be obtained.
import math

math.gcd(24, 36)
>> 12
math.lcm(12, 18)
>> 36
math.nextafter(3, -1)
>> 2.9999999999999996
3 - math.ulp(3)
>> 2.9999999999999996
math.nextafter(3, -1) + math.ulp(3)
>> 3.0 

Now any valid expression can be a decorator ( PEP-0614 )
The restriction is removed from decorators, according to which only a name can act as a decorator and its syntax allowed only separation by dots.

I don’t think that many people thought about the existence of such a restriction, but the pep description gives an example when an innovation makes the code more coherent. By analogy, we can cite
simplified and artificial example:
def a(func):
	def wrapper():
		print('a')
		func()
	return wrapper
	
def b(func):
	def wrapper():
		print('b')
		func()
	return wrapper
	
decorators = [a, b]

@decorators[0] #   3.8        
def some_func():
	print('original')
	
some_func()
>> a
>> original

The unparse method ( bpo-38870 ) was added to the ast module.
As the name implies, it can compile the source string using the ast.AST object. Once, I even wanted to use this and it was surprising that there was no such method.
Example:
import ast
parsed = ast.parse('import pprint; pprint.pprint({"one":1, "two":2})')
unparsed_str = ast.unparse(parsed)
print(unparsed_str)
>> import pprint
>> pprint.pprint({'one': 1, 'two': 2})
exec(unparsed_str)
>> {'one': 1, 'two': 2}

The new functools.TopologicalSorter class for topological sorting of directed acyclic graphs ( bpo-17005 ) The
graph that is passed to the sorter should be a dictionary in which the keys are the vertices of the graph, and the value is an iterable object with predecessors (vertices whose arcs point to the key). As a key, as usual, any hashed type is suitable.
Example:
:

from functools import TopologicalSorter
graph = graph = {8: [3, 7], 11: [5, 7], 2: [11], 9: [8, 11], 10: [3, 11]}
t_sorter = TopologicalSorter(graph)
t_sorted_list = list(t_sorted_list.static_order()) #  ,    ,     static_order.
>> [3, 7, 5, 8, 11, 2, 9, 10]


, TopologicalSorter add. , , .

New statuses that we have been waiting for for so long have been
added to http.HTTPStatus : 103 EARLY_HINTS
418 IM_A_TEAPOT
425 TOO_EARLY

And a few more changes:
  • Built-in types (range, tuple, set, frozenset, list) are accelerated ( PEP-590 )
  • "" .replace ("", s, n) now returns s, not an empty string, for all nonzero n. This is true for both bytes and bytearray.
  • ipaddress now supports parsing IPv6 addresses with destinations.

Since version 3.9 is still in development, and perhaps more changes will be added, I plan to supplement this article as necessary.

You can read in more detail:
docs.python.org/3.9/whatsnew/3.9.html
www.python.org/downloads/release/python-390a6
In general, not to say that future changes are what everyone has been waiting for for a long time and without which it’s impossible to get by, although there are some nice points. And what did you find most interesting in the new version?

UPD:
updated link to the latest release;
added PEP-0585 about type annotation

All Articles