Skip to content
Code Kata
Go back

Python 3.10+: Key Updates by Version

Suggest edit

Table of contents

Open Table of contents

Overview

Python 3.10+ introduced structural pattern matching, improved error messages, and significant typing enhancements. This post covers the key additions that make Python more expressive and type-safe.

Version 3.10

Structural Pattern Matching

The most significant addition — match/case statements with destructuring:

def process_command(command: str) -> str:
    match command.split():
        case ["quit"]:
            return "Goodbye"
        case ["go", direction]:
            return f"Going {direction}"
        case ["get", item] if item != "sword":
            return f"Picked up {item}"
        case _:
            return "Unknown command"

Class Pattern Matching

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

def classify_point(point: Point) -> str:
    match point:
        case Point(x=0, y=0):
            return "origin"
        case Point(x=0, y=y):
            return f"on y-axis at {y}"
        case Point(x=x, y=0):
            return f"on x-axis at {x}"
        case Point():
            return "elsewhere"

Parenthesized Context Managers

with (
    open("input.txt") as infile,
    open("output.txt", "w") as outfile,
):
    outfile.write(infile.read())

Better Error Messages

Python 3.10 dramatically improved error messages with precise locations and suggestions:

# Before: SyntaxError: invalid syntax
# After:  SyntaxError: '{' was never closed

Union Type Syntax with |

def process(value: int | str) -> str:
    return str(value)

Version 3.11

Exception Groups and except*

Handle multiple exceptions simultaneously:

try:
    async with asyncio.TaskGroup() as tg:
        tg.create_task(risky_operation_1())
        tg.create_task(risky_operation_2())
except* ValueError as eg:
    for exc in eg.exceptions:
        print(f"ValueError: {exc}")
except* TypeError as eg:
    for exc in eg.exceptions:
        print(f"TypeError: {exc}")

tomllib — Built-in TOML Parsing

import tomllib

with open("pyproject.toml", "rb") as f:
    config = tomllib.load(f)

print(config["project"]["name"])

Performance Improvements

Python 3.11 is 10-60% faster than 3.10 thanks to the Faster CPython project, including specializing adaptive interpreter and lazy frame objects.

Fine-Grained Error Locations

Tracebacks now point to the exact expression that caused the error:

# Traceback:
#   x['a']['b']['c']['d']
#          ^^^^
# TypeError: 'NoneType' is not subscriptable

Version 3.12

Type Parameter Syntax

New syntax for declaring generics without importing from typing:

# Before
from typing import TypeVar, Generic
T = TypeVar('T')
class Stack(Generic[T]):
    ...

# After (3.12+)
class Stack[T]:
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

Type Aliases with type Statement

type Point = tuple[float, float]
type Matrix[T] = list[list[T]]
type RecursiveList[T] = T | list["RecursiveList[T]"]

F-String Improvements

F-strings can now contain any valid Python expression, including nested f-strings, backslashes, and comments:

# Nested f-strings
print(f"result: {f'{value:.2f}' if value else 'N/A'}")

# Multiline expressions
message = f"Hello {
    name    # inline comment OK
    .upper()
}"

@override Decorator

from typing import override

class Parent:
    def process(self) -> None: ...

class Child(Parent):
    @override
    def process(self) -> None:  # type checker verifies parent has this method
        ...

Version 3.13

Improved Interactive Interpreter

New REPL with multiline editing, color output, and history navigation (based on PyPy’s).

Experimental Free-Threaded Mode

Optional build without the GIL (--disable-gil), enabling true parallel threads:

python3.13t -X gil=0 my_script.py

Experimental JIT Compiler

Copy-and-patch JIT compiler — opt-in, providing modest speedups as a foundation for future optimization.

typing.ReadOnly for TypedDict

from typing import ReadOnly, TypedDict

class Config(TypedDict):
    name: ReadOnly[str]
    debug: bool

config: Config = {"name": "app", "debug": True}
# config["name"] = "other"  # type error
config["debug"] = False  # OK

Deprecation of typing.NamedTuple Old Syntax

New recommended pattern:

from typing import NamedTuple

class Point(NamedTuple):
    x: float
    y: float
    label: str = "default"

Key Takeaways

  1. Pattern matching — Python’s most powerful control flow addition since generators
  2. Type parameter syntax — generics finally feel native (class Stack[T])
  3. Performance — consistent speedups across 3.11-3.13 (Faster CPython, JIT foundations)
  4. Error experience — dramatically better tracebacks and error messages
  5. Typing maturitytype aliases, @override, ReadOnly, union | syntax

Suggest edit
Share this post on:

Previous Post
Python Core Concepts
Next Post
PHP Core Concepts