DEV Community

Cover image for What's new in Python 3.10?
Ashutosh Krishna
Ashutosh Krishna

Posted on

What's new in Python 3.10?

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: 

  1. using data with type and shape (the subject)

  2. evaluating the subject in the match statement

  3. comparing the subject with each pattern in a case statement from top to bottom until a match is confirmed.

  4. executing the action associated with the pattern of the confirmed match

  5. 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 AttributeErrorPyErr_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 method int.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 optional strict 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)