Python 3.10, the latest in-development version of Python, has been released in its second beta version. With the beta release, the feature set for Python 3.10 has been locked down, and intrepid Python developers are encouraged to test their code against the latest builds (although not in a production environment, of course).
Here’s a rundown of all of the major new features in Python 3.10, with discussion of how they can help your code.
Structural Pattern Matching
It is the most anticipated feature of Python 3.10. This feature introduces a switch/case-like feature as we have in other programming languages to Python. In addition to the switch-like feature, Python 3.10 Structural Pattern Matching brings some extra features, making it even more powerful than the usual switch conditional statement.
It has been added in the form of a match statement and case statements of patterns with associated actions. Patterns consist of sequences, mappings, primitive data types as well as class instances.
The generic syntax of pattern matching is:
match subject:
case <pattern_1>:
<action_1>
case <pattern_2>:
<action_2>
case <pattern_3>:
<action_3>
case _:
<action_wildcard>
A match statement takes an expression and compares its value to successive patterns given as one or more case blocks. Specifically, pattern matching operates by:
-
using data with type and shape (the
subject
) -
evaluating the
subject
in thematch
statement -
comparing the subject with each pattern in a
case
statement from top to bottom until a match is confirmed. -
executing the action associated with the pattern of the confirmed match
-
If an exact match is not confirmed, the last case, a wildcard
_
, if provided, will be used as the matching case. If an exact match is not confirmed and a wildcard case does not exist, the entire match block is a no-op.
Let’s look at this example as pattern matching in its simplest form: a value, the subject, being matched to several literals, the patterns. In the example below, status
is the subject of the match statement. The patterns are each of the case statements, where literals represent request status codes. The associated action to the case is executed after a match:
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the Internet"
If the above function is passed a status
of 418, “I’m a teapot” is returned. If the above function is passed a status
of 500, the case statement with _
will match as a wildcard, and “Something’s wrong with the Internet” is returned. Note the last block: the variable name, _
, acts as a wildcard and insures the subject will always match. The use of _
is optional.
You can combine several literals in a single pattern using |
(“or”):
case 401 | 403 | 404:
return "Not allowed"
You can learn more about this feature in official documentation available here.
Parenthesized context managers
Using enclosing parentheses for continuation across multiple lines in context managers is now supported. This allows formatting a long collection of context managers in multiple lines in a similar way as it was previously possible with import statements. For instance, all these examples are now valid:
with (CtxManager() as example):
...
with (
CtxManager1(),
CtxManager2()
):
...
with (CtxManager1() as example,
CtxManager2()):
...
with (CtxManager1(),
CtxManager2() as example):
...
with (
CtxManager1() as example1,
CtxManager2() as example2
):
...
it is also possible to use a trailing comma at the end of the enclosed group:
with (
CtxManager1() as example1,
CtxManager2() as example2,
CtxManager3() as example3,
):
...
Learn more about this feature here.
Better error messages
In previous versions of Python, debugging a code is pretty difficult due to poor error messages. Error messages generated by the Parser from previous versions of Python are not specific. This version brings more precise messages with it.
SyntaxErrors
When parsing code that contains unclosed parentheses or brackets the interpreter now includes the location of the unclosed bracket of parentheses instead of displaying SyntaxError: unexpected EOF while parsing or pointing to some incorrect location. For instance, consider the following code (notice the unclosed ‘{‘):
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
some_other_code = foo()
Previous versions of the interpreter reported confusing places as the location of the syntax error:
File "example.py", line 3
some_other_code = foo()
^
SyntaxError: invalid syntax
but in Python 3.10 a more informative error is emitted:
File "example.py", line 1
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
^
SyntaxError: '{' was never closed
In a similar way, errors involving unclosed string literals (single and triple quoted) now point to the start of the string instead of reporting EOF/EOL.
IndentationErrors
Many IndentationError
exceptions now have more context regarding what kind of block was expecting an indentation, including the location of the statement:
def foo():
if lel:
x = 2
File "<stdin>", line 3
x = 2
^
IndentationError: expected an indented block after 'if' statement in line 2
AttributeErrors
When printing AttributeError
, PyErr_Display()
will offer suggestions of similar attribute names in the object that the exception was raised from:
collections.namedtoplo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'collections' has no attribute 'namedtoplo'. Did you mean: namedtuple?
NameErrors
When printing NameError
raised by the interpreter, PyErr_Display()
will offer suggestions of similar variable names in the function that the exception was raised from:
>>> schwarzschild_black_hole = None
>>> schwarschild_black_hole
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'schwarschild_black_hole' is not defined. Did you mean: schwarzschild_black_hole?
You can learn more about this feature here.
New Type Union Operator
Most of you probably know that Pythn supports type hints. They do not guarantee Type Safety but serve as a useful tool in development.
A new type union operator was introduced which enables the syntax X | Y
. This provides a cleaner way of expressing ‘either type X or type Y’ instead of using typing.Union
, especially in type hints.
In previous versions of Python, to apply a type hint for functions accepting arguments of multiple types, typing.Union
was used:
def square(number: Union[int, float]) -> Union[int, float]:
return number ** 2
Type hints can now be written in a more succinct manner:
def square(number: int | float) -> int | float:
return number ** 2
This new syntax is also accepted as the second argument to isinstance()
and issubclass()
:
>>> isinstance(1, int | str)
True
Learn more about this feature here.
Other Language Changes
- The
int
type has a new methodint.bit_count()
, returning the number of ones in the binary expansion of a given integer, also known as the population count. - The
zip()
function now has an optionalstrict
flag, used to require that all the iterables have an equal length. - Variables can now be declared as type aliases, to allow forward references, more robust errors involving types, and better distinctions between type declarations in scopes.
- OpenSSL 1.1.1 or newer is now required to build CPython. This modernizes one of CPython’s key dependencies.
Read the entire changes in the official documentation here.
Top comments (0)