Python 3.10 New Features — Core Concepts
What changed in Python 3.10
Released in October 2021, Python 3.10 introduced several features that meaningfully changed daily coding. Here’s what matters and why.
Structural pattern matching (PEP 634–636)
The biggest addition. match/case lets you inspect the structure of data — its type, length, and nested values — and branch accordingly.
match command:
case {"action": "buy", "item": item, "qty": qty}:
process_purchase(item, qty)
case {"action": "refund", "order_id": oid}:
process_refund(oid)
case _:
print("Unknown command")
This replaces chains of isinstance checks, dictionary key lookups, and deeply nested if/elif blocks. It’s especially useful for parsing JSON APIs, interpreting AST nodes, and command dispatching.
Six pattern types exist: literal, capture, wildcard, sequence, mapping, and class patterns. Guards (case x if x > 0) add extra conditions.
Better error messages (PEP 657)
Python 3.10 overhauled error reporting. Key improvements:
- Precise column indicators — the caret (
^) points at the exact token that caused the problem, not the start of the line. - Contextual suggestions — forget a colon after
if? Python says “expected ’:’”. Misspell an attribute? It suggests the closest match. - Unclosed bracket tracking — instead of “SyntaxError: unexpected EOF,” it tells you which bracket was never closed and where you opened it.
This alone saves hours of debugging, especially for beginners.
Union type syntax with | (PEP 604)
Type hints became shorter:
# Before
from typing import Union
def process(value: Union[int, str]) -> Union[int, str]:
...
# After (3.10+)
def process(value: int | str) -> int | str:
...
The | operator works in isinstance and issubclass too:
isinstance(value, int | str) # replaces isinstance(value, (int, str))
Parenthesised context managers (BPO-12782)
You can now use parentheses to split multiple context managers across lines:
with (
open("input.txt") as fin,
open("output.txt", "w") as fout,
):
fout.write(fin.read())
Before 3.10, this required backslash line continuation or nesting — both ugly.
Parameter specification variables (PEP 612)
ParamSpec lets you write decorators that preserve the decorated function’s signature in type checkers:
from typing import ParamSpec, Callable, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
def logged(func: Callable[P, T]) -> Callable[P, T]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
This was a game-changer for library authors who write higher-order functions.
TypeAlias (PEP 613)
Explicitly marks a variable as a type alias to avoid confusion:
from typing import TypeAlias
Vector: TypeAlias = list[tuple[float, float]]
Without TypeAlias, type checkers sometimes can’t tell if you’re creating an alias or just assigning a value.
Common misconception
“Pattern matching is just a switch statement.” It’s fundamentally different. A switch compares one value against constants. Pattern matching destructures data — checking types, lengths, keys, and nested shapes simultaneously. It’s closer to what Rust and Haskell do than what C or JavaScript offer.
The one thing to remember: Python 3.10’s pattern matching and improved errors represent a maturity leap — writing Python became more expressive, and debugging became less painful.
See Also
- Python 311 New Features Python 3.11 made everything faster, error messages smarter, and let you catch several mistakes at once instead of stopping at the first one.
- Python 312 New Features Python 3.12 made type hints shorter, f-strings more powerful, and started preparing Python's engine for a world without the GIL.
- Python 313 New Features Python 3.13 finally lets multiple tasks run at the same time for real, added a speed booster engine, and gave the interactive prompt a colourful makeover.
- Python Exception Groups Python's ExceptionGroup is like getting one report card that lists every mistake at once instead of stopping at the first one.
- Python Free Threading Nogil Python has always had a rule that only one thing can happen at a time — free threading finally changes that, like opening extra checkout lanes at the grocery store.