New Features of Python 3.10

Ali Aref
6 min readJan 17, 2022

What is new in Python 3.10?

Python is an interpreted high-level general-purpose programming language. Its design philosophy emphasizes code readability with its use of significant indentation. Its language constructs as well as its object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects

Now Python 3.10 is out! Volunteers have been working on the new version since May 2020 to bring you a better, faster, and more secure Python. As of October 4, 2021, the first official version is available.

So Let’s see some of the Python 3.10 new features.

1- Better Error Tracking

Python is a user friendly-programming language. However some times the interpreter wasn’t much friendly on errors.
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 ‘{‘):
for example if you had coded

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()

On 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.

SyntaxError

SyntaxError Syntax Error exceptions raised by the interpreter will now highlight the full error range of the expression that constitutes the syntax error itself, instead of just where the problem is detected. In this way, instead of displaying (before Python 3.10):

>>> foo(x, z for z in range(10), t, w)
File "<stdin>", line 1
foo(x, z for z in range(10), t, w)
^
SyntaxError: Generator expression must be parenthesized

now Python 3.10 will display the exception as:

>>> foo(x, z for z in range(10), t, w)
File "<stdin>", line 1
foo(x, z for z in range(10), t, w)
^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized

A considerable amount of new specialized messages for SyntaxError exceptions have been incorporated. Some of the most notable ones are as follows:

  • Missing : before blocks
  • Unparenthesised tuples in comprehensions targets
  • Missing commas in collection literals and between expressions
  • Multiple Exception types without parentheses
  • Missing : and values in dictionary literals
  • try blocks without except or finally blocks
  • Usage of = instead of == in comparisons
  • Usage of * in f-strings

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?

2- Structural Pattern Matching

If you’ve used other programming languages like C++, you may have wished Python had the switch statement so you don’t have to go through the long if, elif, elif,…., else statement.
Well, one of the new features of Python 3.10 is the addition of structural pattern matching otherwise known as the switch, case statement which has the following syntax:

match subject:
case <patt1>:
<act1>
case <patt2>:
<act2>
case <patt3>:
<act3>
case _:
<action_default>

3- New Type Union Operator

A new type union operator is 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

4- Default Text Encodings

When you open a text file, the default encoding used to interpret the characters is system dependent. In particular, locale.getpreferredencoding() is used. On Mac and Linux, this usually returns "UTF-8", while the result on Windows is more varied.

You should therefore always specify an encoding when you attempt to open a text file:

with open("some_file.txt", mode="r", encoding="utf-8") as file:
... # Do something with file

If you don’t explicitly specify an encoding, the preferred locale encoding is used, and you could experience that a file that can be read on one computer fails to open on another.

Python 3.7 introduced UTF-8 mode, which allows you to force your programs to use UTF-8 encoding independent of the locale encoding. You can enable UTF-8 mode by giving the -X utf8 command-line option to the python executable or by setting the PYTHONUTF8 environment variable.

In Python 3.10, you can activate a warning that will tell you when a text file is opened without a specified encoding. Consider the following script, which doesn’t specify an encoding:

# mirror.py

import pathlib
import sys

def mirror_file(filename):
for line in pathlib.Path(filename).open(mode="r"):
print(f"{line.rstrip()[::-1]:>72}")

if __name__ == "__main__":
for filename in sys.argv[1:]:
mirror_file(filename)

The program will echo one or more text files back to the console, but with each line reversed. Run the program on itself with the encoding warning enabled:

$ python -X warn_default_encoding mirror.py mirror.py
/home/rp/mirror.py:7: EncodingWarning: 'encoding' argument not specified
for line in pathlib.Path(filename).open(mode="r"):
yp.rorrim #

bilhtap tropmi
sys tropmi

:)emanelif(elif_rorrim fed
:)"r"=edom(nepo.)emanelif(htaP.bilhtap ni enil rof
)"}27>:]1-::[)(pirtsr.enil{"f(tnirp

:"__niam__" == __eman__ fi
:]:1[vgra.sys ni emanelif rof
)emanelif(elif_rorrim

Note the EncodingWarning printed to the console. The command-line option -X warn_default_encoding activates it. The warning will disappear if you specify an encoding—for example, encoding="utf-8"—when you open the file.

There are times when you want to use the user-defined local encoding. You can still do so by explicitly using encoding="locale". However, it’s recommended to use UTF-8 whenever possible. You can check out PEP 597 for more information.

5- 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,
):
...

And for more information
you can read more about Python 3.10 New features on Python Docs.

--

--