Python 3.10 Changes#

New language features#

See Python 3.10: New features for more information about the changes in this section.

Parenthesized context managers#

You can now use enclosing parentheses for continuing lines across multiple lines in context managers:

# Python 3.9
with open('output.log', 'rw') as fout, open('input.csv') as fin:
	fout.write(fin.read())

# Python 3.10
with (open('output.log', 'w') as fout, open('input.csv') as fin):
	fout.write(fin.read())
# or
with (
    CtxManager1() as example1,
    CtxManager2() as example2,
    CtxManager3() as example3,
):

See PEP 617: New PEG parser for CPython for more details.

Better error messages#

Python 3.10 emits more informative messages. For example, SyntaxError exceptions for unclosed brackets now give the correct location and context of the error:

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

Structural pattern matching with match and case#

Python 3.10 introduces match and case statements that let you specify different actions to take, based on which pattern matches a given value:

match value:
    case "hello":
        print(value.upper())
    case ("greeting", content):
        print(f"Greeting is {content}")
    case (label, content):
        print(f"{label}: {content}")
    case _:
        print("bye")

If the wildcard, _, is provided as the last case, it matches any value still not matched after evaluating previous cases. If the wildcard case is not supplied, the match block is a null operation for unmatched values.

You can combine several literals in a single pattern using | (“or”):

case (label, content) | {"label": label, "content": content}:
    print(f"{label}: {content}")

See Python 3.10: Structural pattern matching for more information.

Type hints#

Use | (pipe character) operator instead of Union#

Python 3.10 lets you use the pipe character | as a shortcut for Union in type hints:

# Python 3.9
def func(num: Union[int, float]) -> Union[int, float]:
	return num + 10

# Python 3.10
def func(num: int | float) -> int | float:
	return num + 10

See Python 3.10: New type union operator for more details.

TypeAlias#

Type aliases (TypeAlias) have been added to the typing module.

Type aliases are user-specified types which may be as complex as any type hint, and are specified with a simple variable assignment at the top level of a module.

Python 3.10 formalizes a way to explicitly declare an assignment as a type alias:

from typing import TypeAlias

StrCache: TypeAlias = 'Cache[str]'  # a type alias

def func() -> StrCache:
...

User-defined type guards#

Type guards (TypeGuard) have been added to the typing module, allowing you to implement type narrowing with user-defined type guard functions.

To do this, use TypeGuard to annotate the return value of a type guard function:

from typing import TypeGuard


def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
    '''Determines whether all objects in the list are strings'''
    return all(isinstance(x, str) for x in val)

def func(val: list[object]):
    if is_str_list(val):
        # Type of ``val`` is narrowed to ``list[str]``.
        print(" ".join(val))
    else:
        # Type of ``val`` remains as ``list[object]``.
        print("Not a list of strings!")

The form def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ... means that if is_str_list(val) returns True, then val narrows from list[object] to list[str].

Type hints for callable objects#

Python 3.10 adds new options for creating type hints for callables:

  • Parameter specification variables forward the parameter types of one callable to another callable, expressing paramater type dependence.

  • Concatenate is used in conjunction with parameter specification variables to annotate a higher order callable which adds or removes the parameters of another callable.

See Python 3.10: Parameter specification variables for more information.

Specifying text encoding#

When working with TextIOWrapper and open, Python uses the locale’s default encoding if no encoding is specified. This can can lead to bugs when working across platforms with conflicting default encodings. For example:

# May not work on Windows when non-ASCII characters in the file.
with open("README.md") as f:
    long_description = f.read()

Python 3.10 gives you several ways to avoid creating this kind of bug:

  • Specify encodings explicitly. For example: long_description = f.read(encoding="utf-8")

  • Use the PYTHONWARNDEFAULTENCODING environment variable (-X warn_default_encoding from the command line) to emit an EncodingWarning when the locale’s default encoding is being used.

  • Where using the locale’s default encoding is the desired behaviour, pass encoding="locale".

See Python documentation: Text encoding for more information.

Other language changes#

See Python 3.10: Other language changes for more information about the changes in this section.

  • The int type has a new method, int.bit_count, returning the number of ones in the binary expansion of a given integer.

  • Python 3.10 adds strict zipping (PEP 618: Strict zipping). The zip function now has an optional strict flag which, when True, requires that the zipped iterables all have equal length and raises a ValueError if one iterable is exhausted before the others:

    for item in zip(range(3), ['fee', 'fi', 'fo', 'fum'], strict=True):
        print(item)
    ...
    (0, 'fee')
    (1, 'fi')
    (2, 'fo')
    Traceback (most recent call last):
    ...
    ValueError: zip() argument 2 is longer than argument 1
    
  • Two new builtins, aiter and anext, have been added to provide asynchronous counterparts to iter and next respectively.

  • Static methods (@staticmethod) and class methods (@classmethod) now inherit method attributes (__module__, __name__, __qualname__, __doc__, __annotations__) and have a new __wrapped__ attribute.

    Additionally, static methods are now callable as regular functions:

    class Trade():
    
        @staticmethod
        def calculate_fee(trade_value):
            return trade_value * 0.001
    
        reference = {1: calculate_fee}
    
    Trade.reference[1](2718281)
    
    # Python 3.9 ->
    # TypeError: 'staticmethod' object is not callable
    
    # Python 3.10 ->
    # 2718.281
    

    See Python bug 43682 for more information.

  • For both float and decimal.Decimal types, hashes of NaN values now depend on object identity. Previously, both float and Decimal NaN values hashed to 0, causing hash collisions and resulting in poor runtime performance.

    In Python 3.9:

    # Python 3.9
    f1 = float("nan")
    f2 = float("nan")
    
    print(f1, f2)                  # nan nan
    print(f1 == f2)                # False
    print(hash(f1))                # 0
    print(hash(f2))                # 0
    print(hash(f1) == hash(f2))    # True
    

    In Python 3.10:

    # Python 3.10
    
    f1 = float("nan")
    f2 = float("nan")
    
    print(f1, f2)                  # nan nan
    print(f1 == f2)                # False
    print(hash(f1))                # 305075651
    print(hash(f2))                # 276643293
    print(hash(f1) == hash(f2))    # False
    

    See Python bug 87641 for more information.

  • Class and module objects now lazy-create empty annotations dicts on demand.

    Previously, when trying to access the __annotations__ attribute of a class, you might accidentally get the type hints dictionary from the base class instead of the derived class:

    class Base:
        a: int = 3
        b: str = 'abc'
    
    class Derived(Base):
        pass
    
    print(Derived.__annotations__)
    # Python 3.9: annotations dict from `Base`
    # {'a': 'int', 'b': 'str'}
    
    # Python 3.10: lazily-created empty annotations dict
    # {}
    

    See Python documentation: Annotations for more information.

  • object.__ipow__ falls back to object.__pow__ and object.__rpow__ if it returns NotImplemented.

  • A new function, mapping, has been added to dict views such as those returned by dict.items and dict.keys to help with introspection. The mapping function returns a MappingProxyType.

  • Builtin and extension functions that take integer arguments no longer accept Decimal, Fraction or other objects that can only be converted to integers with a loss.

  • Assignment expressions can now be used unparenthesized within set literals and set comprehensions, as well as in sequence indexes (but not slices). See Python bug 84811 for more information.

  • Functions have a new __builtins__ attribute which is used to look for builtin symbols when a function is executed, instead of looking into __globals__['__builtins__'].

Annotations#

  • When using __future__.annotations:

    • Only annotations for simple names will take effect.

    • Annotations consisting of yield, yield from, await, or named expressions are now forbidden.

    • Annotations consisting of unbound variables, super, and other expressions that might alter the processing of the symbol table have no effect.

Errors#

  • Deleting the __debug__ constant now raises a SyntaxError instead of a NameError.

  • SyntaxError exceptions now have end_lineno and end_offset attributes (set to None if not determined).

Optimizations#

See Python 3.10: Optimizations for more information about the changes in this section.

  • Constructors for str, bytes, and bytearray objects are now faster (around 30%-40% for small objects).

  • The Python 3.10 interpreter gains 30%-40% improved performance from compilation with gcc’s -fno-semantic-interposition option. See Red Hat Enterprise Linux: Faster Python for more information.

  • When using stringized annotations, annotations dicts for functions are no longer created when the function is created. Instead, they are stored in a compact form (tuple of strings), and the function object lazily converts this into the annotations dict on demand (when func.__annotation__ is accessed).

    This optimization cuts the CPU time needed to define an annotated function by half.

  • Command startup with runpy, invoked via python3 -m module-name, is now on average 1.4x faster.

  • Substring search functions such as str1 in str2 and str2.find(str1) now sometimes use Crochemore & Perrin’s “Two-Way” string searching algorithm to avoid quadratic behavior on long strings. See [Python bug 86138][bug_86138] for more information.

  • The LOAD_ATTR instruction now uses the new per-opcode caching mechanism. It is about 36% faster now for regular attributes and 44% faster for slots.

  • New output buffer management code, along with the addition of the readall function to _compression.DecompressReader, gives performance increases for compression and decompression:

    • bz2 decompression: 1.09x ~ 1.17x faster

    • lzma decompression: 1.20x ~ 1.32x faster

    • zlib (gzip) decompression: 1.11x ~ 1.18x faster

    In the SigTech framework, zlib is used in serde.py and ray.py.

  • Micro-optimizations to _PyType_Lookup improve type attribute cache lookup performance in the common case of cache hits. This makes the interpreter 1.04 times faster on average.

  • The following built-in functions now support the faster PEP 590 vectorcall calling convention:

  • BZ2File performance is improved by removing internal RLock. This makes the BZ2File thread unsafe in the face of multiple simultaneous readers or writers, like the equivalent classes in gzip and lzma.

Improved modules#

See Python 3.10: Improved modules for more information about the changes in this section.

collections.abc#

  • In the collections.abc module, collections.abc.Callable[[int, str], str] will now have __args__ as (int, str, str) instead of the previous ([int, str], str).

    The __args__ attribute of the parameterized generic for collections.abc.Callable now flattens type parameters similarly to typing.Callable.

  • A TypeError may be raised for invalid forms of parameterizing collections.abc.Callable, which may have passed silently in Python 3.9.

enum#

In enum, Enum.__repr__ now returns enum_name.member_name and Enum.__str__ now returns member_name.

Standard library enums available as module constants have a repr of module_name.member_name.

typing#

For major changes to the typing module, see Python 3.10: New features related to type hints.

  • In typing.Literal:

    • Literal now de-duplicates parameters.

    • Equality comparisons between Literal objects are now order independent.

    • Literal comparisons now respect types. For example, Literal[0] == Literal[False] previously evaluated to True. It is now False. To support this change, the internally used type cache now supports differentiating types.

    • Literal objects will now raise a TypeError exception during equality comparisons if any of their parameters are not hashable. Note that declaring Literal with unhashable parameters will not throw an error:

      from typing import Literal
      Literal[{0}]
      Literal[{0}] == Literal[{False}]
      
      # Traceback (most recent call last):
      # File "<stdin>", line 1, in <module>
      # TypeError: unhashable type: 'set'
      
  • A new function, is_typeddict, introspects whether an annotation is a [TypedDict][pdoc_typeddict].

  • Subclasses of typing.Protocol which only have data variables declared will now raise a TypeError when checked with isinstance unless they are decorated with the runtime_checkable decorator.

    Previously, these checks passed silently. For runtime protocols, decorate subclasses with runtime_checkable.

  • Importing from the typing.io and typing.re submodules will now emit DeprecationWarning; anything belonging to those submodules should be imported directly from typing instead.

See PEP 484: Type hints for more information.

Deprecations#

See Python 3.10: Deprecated for more information about the changes in this section.

  • A deprecation warning is raised if a numeric literal is immediately followed by one of these keywords:

    • and

    • else

    • for

    • if

    • in

    • is

    • or

    For example, the code below is valid syntax prior to Python 3.10:

    0in x
    1or x
    0if 1else 2
    [0x1for x in y]
    

    In future releases, the deprecation warning will be changed to a syntax warning and finally to a syntax error.

  • The entire distutils namespace is deprecated, to be removed in Python 3.12.

  • Non-integer arguments to random.randrange are deprecated; the ValueError is deprecated in favor of a TypeError.

  • pathlib.Path.link_to is deprecated and slated for removal in Python 3.12; use pathlib.Path.hardlink_to instead.

  • Certain deprecated ssl features are slated for removal in Python 3.11; see Python 3.10: Changes to ssl module.

  • cgi.log is deprecated and slated for removal in Python 3.12.

Threading deprecations#

  • The following threading methods are now deprecated and replaced:

    • threading.currentThread replaced by threading.current_thread

    • threading.activeCount replaced by threading.active_count

    • threading.Condition.notifyAll replaced by threading.Condition.notify_all

    • threading.Event.isSet replaced by threading.Event.is_set

    • threading.Thread.setName replaced by threading.Thread.name

    • threading.thread.getName replaced by threading.Thread.name

    • threading.Thread.isDaemon replaced by threading.Thread.daemon

    • threading.Thread.setDaemon replaced by threading.Thread.daemon

  • The threading debug (PYTHONTHREADDEBUG environment variable) is deprecated in Python 3.10 and will be removed in Python 3.12.

Import syntax, import library, and import system deprecations#

  • Starting with Python 3.10, outdated import semantics will gradually start to raise DeprecationWarning or ImportWarning, including:

    • find_loader

    • find_module

    • load_module

    • module_repr

    • __package__

    • __loader__

    • __cached__

  • Importing from the typing.io and typing.re submodules will now emit DeprecationWarning. These submodules will be removed in a future version of Python. Anything belonging to these submodules should be imported directly from typing instead.

  • zimport.zipimporter.load_module has been deprecated in preference for exec_module.

  • Deprecated importlib load_module methods now raise a DeprecationWarning; use exec_module instead.

  • Use of importlib.abc.Loader.load_module by the import system now triggers an ImportWarning, as exec_module is preferred.

  • The import system now triggers an ImportWarning when using importlib.abc.MetaPathFinder.find_module and importlib.abc.PathEntryFinder.find_module, as the respective modules’ find_spec methods are preferred. To aid in porting, you can use importlib.util.spec_from_loader.

  • The import system now triggers an ImportWarning when using importlib.abc.PathEntryFinder.find_loader, as the preferred method is importlib.abc.PathEntryFinder.find_spec. To aid in porting, you can use importlib.util.spec_from_loader.

  • importlib.abc.Finder is deprecated (including its sole method, find_module). Both importlib.abc.MetaPathFinder and importlib.abc.PathEntryFinder no longer inherit from the class; users should inherit from one of these two classes as appropriate instead.

  • importlib.abc.Loader.module_repr, importlib.machinery.FrozenLoader.module_repr, and importlib.machinery.BuiltinLoader.module_repr are deprecated and slated for removal in Python 3.12.

  • Implementations of importlib.abc.MetaPathFinder.find_module will raise a DeprecationWarning:

    • importlib.abc.MetaPathFinder.find_module

    • importlib.abc.PathEntryFinder.find_loader

    • importlib.abc.PathEntryFinder.find_module

    • importlib.machinery.BuiltinImporter.find_module

    • importlib.machinery.FrozenImporter.find_module

    • importlib.machinery.PathFinder.find_module

    • importlib.machinery.WindowsRegistryFinder.find_module

    • importlib.machinery.FileFinder.find_loader

    • importlib.machinery.FileFinder.find_module

    • importlib.machinery.FileFinder.find_loader

    These methods are slated for removal in Python 3.12.

  • The following deprecations are slated for removal in Python 3.12:

    • imp

    • importlib.find_loader

    • importlib.util.set_package_wrapper

    • importlib.util.set_loader_wrapper

    • importlib.util.module_for_loader

    • pkgutil.ImpImporter

    • pkgutil.ImpLoader

  • The import system now uses the __spec__ attribute on modules before falling back on module_repr for a module’s __repr__ method.

sqlite deprecations#

  • Obsolete and undocumented sqlite3.OptimizedUnicode is now deprecated.

  • sqlite3.enable_shared_cache is now deprecated, scheduled for removal in Python 3.12. If a shared cache must be used, open the database in URI mode using the cache=shared query parameter.

See the SQLite3 documentation for more details.

Removals#

See Python 3.10: Removed for more information about the changes in this section.

  • Removed deprecated aliases to Collections Abstract Base Classes from collections module.

  • Removed parameter loop from most of asyncio’s high-level API; see Python 3.10: Optimizations.

  • Removed the following special methods from complex class:

    • complex.__divmod__

    • complex.__int__

    • complex.__float__

    • complex.__floordiv__

    • complex.__mod__

    • complex.__rdivmod__

    • complex.__rfloordiv__

    • complex.__rmod__

  • Removed ParserBase.error method from the private and undocumented _markupbase module.

  • Removed unicodedata.ucnhash_CAPI attribute; related private _PyUnicode_Name_CAPI structure is moved to the internal C API.

  • Removed deprecated parser module in favor of new PEG parser. Removed all C source and header files only used by the old parser, including node.h, parser.h, graminit.h and grammar.h.

  • Removed the following deprecated public C API functions:

    • PyParser_SimpleParseStringFlags

    • PyParser_SimpleParseStringFlagsFilename

    • PyParser_SimpleParseFileFlags

    • PyNode_Compile

  • Removed deprecated formatter module.

  • Removed unnecessary function PyModule_GetWarningsModule.