Latest in branch 3.10
3.10.20
Released 03 Mar 2026
(3 months ago)
SoftwarePython
Version3.10
Status
Supported
Initial release3.10.0
04 Oct 2021
(4 years ago)
Latest release3.10.20
03 Mar 2026
(3 months ago)
End of life04 Oct 2026
(Ends in 3 months)
Source codehttps://github.com/python/cpython/tree/v3.10.20
Documentationhttps://docs.python.org/release/3.10.20/
Downloadhttps://www.python.org/downloads/release/python-31020/
Python 3.10 ReleasesView full list

What Is New in Python 3.10

Python 3.10 introduces structural pattern matching, the pipe operator for union types, parenthesized context managers, and significantly more precise error messages. Performance improves roughly 10% over 3.9 due to reduced frame overhead and optimized comprehensions.

Category Change PEP / Reference
New Syntax Structural pattern matching -- match/case PEP 634, 635, 636
New Syntax Union types with | operator: int | str PEP 604
New Syntax Parenthesized context managers across multiple lines --
Error Messages Caret (^) pointing to exact error location; NameError suggests similar names --
Typing ParamSpec, TypeGuard, TypeAlias added to typing PEP 612, 647
Standard Library pathlib.Path.is_relative_to(); zip(..., strict=True) --
Performance ~10% average speedup over Python 3.9 --
Deprecated distutils, parser module, array 'u' type code --
Removed formatter, symbol, parser modules; old C APIs --

How Does Structural Pattern Matching Work in Python 3.10?

The match statement compares a subject against a series of patterns. Unlike a switch/case in other languages, Python patterns can destructure sequences, mappings, and class instances -- not just compare literals. Guards (if) can be added to any case arm.

match command:
    case ["quit"]:
        quit()
    case ["move", x, y]:
        move_player(int(x), int(y))
    case ["attack", target] if target in enemies:
        attack(target)
    case _:
        print(f"Unknown command: {command}")

Pattern types include: literals, capture variables, sequences ([a, b, *rest]), mappings ({"key": value}), class patterns (Point(x=0, y=y)), OR patterns (p1 | p2), and the wildcard _. In practice, this replaces long if/elif chains when dispatching on data shape -- a very common pattern in parsers, command handlers, and API response processing.

What Changed About Type Hints in Python 3.10?

Union Types with the | Operator (PEP 604)

The pipe operator replaces Union[X, Y] for type annotations and works at runtime -- isinstance(x, int | str) now works without importing from typing.

# Before 3.10
from typing import Union, Optional
def parse(value: Union[int, str]) -> Optional[int]: ...

# Python 3.10+
def parse(value: int | str) -> int | None: ...

ParamSpec and TypeGuard (PEP 612, 647)

ParamSpec allows type-safe decoration of functions while preserving parameter types -- essential for writing typed decorators that pass arguments through. TypeGuard narrows types in conditional branches when a function confirms an object's type.

from typing import ParamSpec, TypeVar, Callable
P = ParamSpec("P")
R = TypeVar("R")

def logged(fn: Callable[P, R]) -> Callable[P, R]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print(f"calling {fn.__name__}")
        return fn(*args, **kwargs)
    return wrapper

How Did Python 3.10 Improve Error Messages?

Python 3.10 added precise column indicators -- a caret (^) under the exact offending token -- so errors show not just the line but where on the line the problem is. NameError now suggests similarly-named variables when a name is not found, and SyntaxError identifies unclosed brackets at their opening position rather than at the end of the file.

  File "example.py", line 3
    result = x +
                ^
SyntaxError: invalid syntax. Perhaps you forgot a comma?

These changes reduce the time spent staring at error messages trying to locate the actual problem -- especially in deeply nested expressions.

What Other Language and Library Changes Landed in 3.10?

  • Parenthesized context managers: with (open("a") as f, open("b") as g): -- multi-line without backslashes.
  • zip(..., strict=True) raises ValueError if iterables have different lengths -- catches a common silent bug.
  • pathlib.Path.is_relative_to(other) and cleaner Path.relative_to().
  • int.bit_count() -- counts set bits in an integer without converting to string.
  • typing.TypeAlias for explicitly declaring type aliases vs plain assignments.
  • statistics.covariance() and statistics.correlation() added.

What Was Deprecated or Removed in Python 3.10?

  • distutils deprecated -- migrate to setuptools or build before 3.12 when it is removed.
  • parser module deprecated and then removed (was already unusable since the PEG parser replaced LL(1)).
  • formatter and symbol modules removed.
  • array type code 'u' deprecated -- use str for Unicode text.
  • Old C APIs removed: PyEval_CallObject and related pre-vectorcall APIs.
  • Passing non-path objects to Path methods deprecated.

FAQ

Is match/case the same as a switch statement in other languages?
No -- it is structurally richer. A match arm can bind variables from inside a sequence or mapping (destructuring), test class shapes, and chain OR patterns. A traditional switch only compares a value to constants. The closest analogy is Scala or Rust pattern matching, not C's switch.

Can I use int | str in isinstance() at runtime, not just in type checkers?
Yes. isinstance(x, int | str) works at runtime in Python 3.10+. The | operator on types creates a types.UnionType object that isinstance and issubclass understand natively. No from typing import Union needed.

Does zip(strict=True) affect performance?
Negligibly for successful cases -- it only adds a check after all iterators are exhausted. The overhead is a single comparison at the end. For the cases where lengths differ and you want a ValueError, the savings in debugging time far outweigh any cost.

Do I need to update all Union[X, Y] annotations to X | Y immediately after upgrading to 3.10?
No. Union[X, Y] still works and is not deprecated. The | syntax is an alternative that is cleaner in new code. You can migrate gradually or keep the old style -- both are valid indefinitely.

Does parenthesized context managers support trailing commas?
Yes. with (open("a") as f, open("b") as g,): is valid -- the trailing comma follows the same rule as function arguments and collection literals. This is fine style when you add or remove managers frequently and want consistent diffs.

Releases In Branch 3.10

VersionRelease date
3.10.2003 Mar 2026
(3 months ago)
3.10.1909 Oct 2025
(7 months ago)
3.10.1803 Jun 2025
(1 year ago)
3.10.1708 Apr 2025
(1 year ago)
3.10.1603 Dec 2024
(1 year ago)
3.10.1507 Sep 2024
(1 year ago)
3.10.1419 Mar 2024
(2 years ago)
3.10.1324 Aug 2023
(2 years ago)
3.10.1206 Jun 2023
(3 years ago)
3.10.1104 Apr 2023
(3 years ago)
3.10.1007 Feb 2023
(3 years ago)
3.10.906 Dec 2022
(3 years ago)
3.10.811 Oct 2022
(3 years ago)
3.10.705 Sep 2022
(3 years ago)
3.10.601 Aug 2022
(3 years ago)
3.10.506 Jun 2022
(4 years ago)
3.10.423 Mar 2022
(4 years ago)
3.10.316 Mar 2022
(4 years ago)
3.10.213 Jan 2022
(4 years ago)
3.10.106 Dec 2021
(4 years ago)
3.10.004 Oct 2021
(4 years ago)
3.10.0rc207 Sep 2021
(4 years ago)
3.10.0rc102 Aug 2021
(4 years ago)
3.10.0b410 Jul 2021
(4 years ago)
3.10.0b317 Jun 2021
(4 years ago)
3.10.0b231 May 2021
(5 years ago)
3.10.0b103 May 2021
(5 years ago)
3.10.0a705 Apr 2021
(5 years ago)
3.10.0a601 Mar 2021
(5 years ago)
3.10.0a502 Feb 2021
(5 years ago)
3.10.0a404 Jan 2021
(5 years ago)
3.10.0a307 Dec 2020
(5 years ago)
3.10.0a203 Nov 2020
(5 years ago)
3.10.0a105 Oct 2020
(5 years ago)