Modern Python — New Features in Python 3.10–3.13
A practical tour of major features introduced from Python 3.10 through 3.13.
Python 3.10: Structural Pattern Matching (match-case)
# Basic value matching
def classify_http_status(status: int) -> str:
match status:
case 200:
return "OK"
case 201:
return "Created"
case 400:
return "Bad Request"
case 401 | 403: # OR pattern
return "Auth error"
case code if 500 <= code < 600: # Guard condition
return f"Server error: {code}"
case _:
return "Unknown status"
print(classify_http_status(200)) # OK
print(classify_http_status(403)) # Auth error
print(classify_http_status(503)) # Server error: 503
# Sequence pattern
def process_command(command: list[str]) -> str:
match command:
case ["quit"]:
return "Quit"
case ["go", direction]:
return f"Going {direction}"
case ["go", direction, speed]:
return f"Going {direction} at {speed}"
case ["help", *topics]:
return f"Help: {', '.join(topics) or 'all'}"
case []:
return "Empty command"
case _:
return f"Unknown command: {command}"
print(process_command(["go", "north"])) # Going north
print(process_command(["go", "east", "fast"])) # Going east at fast
print(process_command(["help", "move", "look"])) # Help: move, look
# Mapping (dict) pattern
def handle_event(event: dict) -> str:
match event:
case {"type": "click", "x": x, "y": y}:
return f"Click at ({x}, {y})"
case {"type": "keypress", "key": key, "ctrl": True}:
return f"Ctrl+{key}"
case {"type": "keypress", "key": key}:
return f"Key pressed: {key}"
case {"type": t, **rest}:
return f"Unknown event: {t}, extras: {rest}"
print(handle_event({"type": "click", "x": 100, "y": 200}))
print(handle_event({"type": "keypress", "key": "S", "ctrl": True}))
# Class pattern
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
@dataclass
class Circle:
center: Point
radius: float
@dataclass
class Rectangle:
top_left: Point
bottom_right: Point
def describe_shape(shape) -> str:
match shape:
case Circle(center=Point(x=0, y=0), radius=r):
return f"Circle at origin, radius={r}"
case Circle(center=c, radius=r):
return f"Circle: center=({c.x},{c.y}), radius={r}"
case Rectangle(top_left=Point(x=x1, y=y1), bottom_right=Point(x=x2, y=y2)):
return f"Rectangle: ({x1},{y1}) to ({x2},{y2})"
print(describe_shape(Circle(Point(0, 0), 5.0)))
print(describe_shape(Circle(Point(3, 4), 2.0)))
print(describe_shape(Rectangle(Point(0, 0), Point(10, 5))))
Python 3.10: X | Y Union Syntax
# Before: Union[str, None], Optional[str]
# Now: str | None (Python 3.10+)
def find_user(user_id: int) -> dict | None:
users = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
return users.get(user_id)
def process_value(value: int | str | list) -> str:
return str(value)
# Also works with isinstance at runtime
def format_value(v: int | float | str) -> str:
if isinstance(v, int | float): # Python 3.10+: | works in isinstance too
return f"{v:.2f}" if isinstance(v, float) else str(v)
return v.strip()
print(format_value(42)) # 42
print(format_value(3.14)) # 3.14
print(format_value(" hi ")) # hi
# Type aliases
Vector = list[float]
Matrix = list[Vector]
Scalar = int | float
Numeric = int | float | complex
Python 3.11: Exception Groups and ExceptionGroup
# Python 3.11+: ExceptionGroup — handle multiple exceptions at once
def process_items(items: list[int]) -> list[int]:
errors = []
results = []
for item in items:
try:
if item < 0:
raise ValueError(f"Negative value: {item}")
if item == 0:
raise ZeroDivisionError("Division by zero")
results.append(100 // item)
except (ValueError, ZeroDivisionError) as e:
errors.append(e)
if errors:
raise ExceptionGroup("Processing errors", errors)
return results
try:
process_items([5, -3, 0, 2, -1])
except* ValueError as eg:
print(f"ValueError occurred {len(eg.exceptions)} times:")
for e in eg.exceptions:
print(f" - {e}")
except* ZeroDivisionError as eg:
print(f"ZeroDivisionError occurred {len(eg.exceptions)} times")
# Python 3.11+: add_note() — attach notes to exceptions
try:
x = int("not_a_number")
except ValueError as e:
e.add_note("The user-provided value is not a number.")
e.add_note("Valid format: integers only (e.g., 1, 42, -5)")
raise
Python 3.11: Self Type
from typing import Self
class Builder:
def __init__(self) -> None:
self._parts: list[str] = []
def add(self, part: str) -> Self: # Self: return current class type
self._parts.append(part)
return self
def build(self) -> str:
return " ".join(self._parts)
class ExtendedBuilder(Builder):
def add_many(self, *parts: str) -> Self: # Self: returns ExtendedBuilder
for part in parts:
self.add(part)
return self
# Type inference is accurate: returns ExtendedBuilder
builder = ExtendedBuilder()
result = builder.add("Hello").add_many("World", "!").build()
print(result) # Hello World !
Python 3.12: Type Parameter Syntax (PEP 695)
# Python 3.12+: new generic syntax
# Before: from typing import TypeVar, Generic; T = TypeVar('T')
# Now: cleaner type[T] syntax
# Generic function
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
# Generic class
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 alias (Python 3.12+)
type Vector = list[float]
type Matrix = list[Vector]
type Callback[T] = (T) -> None
# TypeVar with constraints
def double[T: (int, float)](x: T) -> T:
return x * 2
print(double(5)) # 10
print(double(3.14)) # 6.28
Python 3.12: @override Decorator
from typing import override
class Animal:
def speak(self) -> str:
return "..."
def move(self) -> str:
return "Moving"
class Dog(Animal):
@override
def speak(self) -> str: # Type checker verifies speak() exists in parent
return "Woof!"
@override
def moove(self) -> str: # Typo! Type checker will warn about this
return "Running!"
Python 3.13: Interactive Interpreter & More
# Python 3.13: copy.replace() — modify immutable objects
import copy
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
p2 = copy.replace(p, x=10.0) # Returns new Point(10.0, 2.0)
print(p) # Point(x=1.0, y=2.0)
print(p2) # Point(x=10.0, y=2.0)
# Python 3.11+: tomllib — TOML parsing in the standard library
import tomllib
toml_content = b"""
[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "Sample project"
[tool.poetry.dependencies]
python = "^3.12"
pydantic = "^2.0"
[database]
host = "localhost"
port = 5432
name = "mydb"
"""
config = tomllib.loads(toml_content.decode())
print(f"Project: {config['tool']['poetry']['name']}")
print(f"DB host: {config['database']['host']}")
# Reading a pyproject.toml file
# with open("pyproject.toml", "rb") as f:
# config = tomllib.load(f)
Version Feature Summary
# Python 3.10 highlights
# - Structural pattern matching (PEP 634) — match-case
# - Union type shorthand (PEP 604) — X | Y
# - Better error messages (SyntaxError improvements)
# - Parenthesized context managers: (with a as x, b as y:)
# Python 3.11 highlights
# - ExceptionGroup + except* (PEP 654)
# - Self type (PEP 673)
# - LiteralString type (PEP 675)
# - Required/NotRequired for TypedDict (PEP 655)
# - Significant performance improvements (~25% faster, CPython optimizations)
# - tomllib added to standard library (PEP 680)
# Python 3.12 highlights
# - New generic syntax: type aliases, type[T], TypeVarTuple (PEP 695)
# - @override decorator (PEP 698)
# - f-string expression improvements (same quotes, multiline allowed)
# - Additional performance improvements
# Python 3.13 highlights
# - Improved interactive interpreter (colored output, multiline editing)
# - copy.replace() (PEP 759)
# - Experimental JIT compiler
# - Experimental free-threaded CPython (GIL removal, PEP 703)
Pro Tip: Improved f-strings (Python 3.12+)
# Python 3.12+: same quote types can be used inside f-strings
items = ["apple", "banana", "cherry"]
# Python 3.12+: no more quote conflicts
result = f"Items: {", ".join(items)}"
print(result)
# Nested f-strings
value = 42
msg = f"Value: {f"{value:08b}"} (binary)" # Value: 00101010 (binary)
print(msg)
Summary
| Version | Key Feature | Keyword |
|---|---|---|
| 3.10 | Structural pattern matching | match-case |
| 3.10 | Union type shorthand | X | Y |
| 3.11 | Multiple exception handling | ExceptionGroup, except* |
| 3.11 | Self-returning type | Self |
| 3.11 | TOML parsing | tomllib |
| 3.12 | Simplified generic syntax | type[T], type alias |
| 3.12 | Override validation | @override |
| 3.13 | Experimental JIT/GIL-free | Performance |
Modern Python evolves steadily in three directions: type safety, expressiveness, and performance.