PythonStory_Interesting.html
copyright © James Fawcett
Revised: 04/26/2026
11.0 Prologue
This final chapter explores advanced Python features that you will encounter in
production codebases and that reward deeper study: cooperative async programming,
metaclass machinery, the descriptor protocol, custom context managers, and structural
pattern matching.
11.1 Async / Await
Python's asyncio library provides cooperative multitasking on a single
thread. An async def function is a coroutine; calling it
returns a coroutine object. Use await to suspend until the awaited work
completes:
import asyncio, httpx
async def fetch(url: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.get(url)
return resp.text
async def main() -> None:
urls = [
"https://example.com",
"https://python.org",
]
tasks = [asyncio.create_task(fetch(u)) for u in urls]
results = await asyncio.gather(*tasks)
for r in results:
print(len(r), "chars")
asyncio.run(main())
Key rules:
- await can only appear inside async def functions.
- CPU-bound work blocks the event loop; offload to loop.run_in_executor().
- asyncio.gather() runs tasks concurrently; asyncio.create_task()
schedules them immediately.
11.2 Context Managers
Any object that implements __enter__ and __exit__ can be
used in a with statement. The contextlib module makes it
easy to create context managers from generator functions:
from contextlib import contextmanager
import time
@contextmanager
def timer(label: str):
start = time.perf_counter()
try:
yield # body of the with-block runs here
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}s")
with timer("sorting"):
data = sorted(range(1_000_000), reverse=True)
contextlib.suppress(ExcType) silences specific exceptions.
contextlib.ExitStack manages a dynamic number of context managers.
11.3 Descriptors
A descriptor is an object that defines __get__,
__set__, and/or __delete__. When placed as a class attribute,
these methods intercept attribute access on instances. @property is
built on descriptors:
class Validated:
"""Descriptor that rejects non-positive values."""
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, f"_{self.name}")
def __set__(self, obj, value):
if value <= 0:
raise ValueError(f"{self.name} must be positive, got {value}")
setattr(obj, f"_{self.name}", value)
class Rectangle:
width = Validated()
height = Validated()
def __init__(self, w: float, h: float) -> None:
self.width = w
self.height = h
11.4 Metaclasses
A metaclass is the class of a class. Just as an instance is built by
its class, a class is built by its metaclass. The default metaclass is type.
Override it to customize class creation — enforcing conventions, registering
subclasses, or injecting methods:
class RegistryMeta(type):
_registry: dict = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if bases: # skip the base class itself
mcs._registry[name] = cls
return cls
class Plugin(metaclass=RegistryMeta):
pass
class AudioPlugin(Plugin): pass
class VideoPlugin(Plugin): pass
print(RegistryMeta._registry)
# {'AudioPlugin': <class '...'>, 'VideoPlugin': <class '...'>}
In modern Python, __init_subclass__ and class decorators often provide a
simpler alternative to a full metaclass.
11.5 Structural Pattern Matching
Introduced in Python 3.10, match / case destructures data structures and
matches against patterns, far beyond what if/elif chains can express:
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def classify(obj):
match obj:
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(x=x, y=y) if x == y:
return f"on diagonal at {x}"
case Point():
return "other point"
case {"action": action, "data": data}:
return f"dict: {action}"
case [first, *rest]:
return f"list starting with {first}"
case _:
return "unknown"
11.6 Walrus Operator and Other Modern Syntax
Walrus operator := (Python 3.8+) assigns and tests in
one expression, reducing repeated function calls in loops and comprehensions:
import re
data = ["100px", "20em", "not a size", "50px"]
sizes = [m.group() for s in data if (m := re.search(r"\d+px", s))]
# ['100px', '50px']
# read chunks until empty
with open("large.txt") as f:
while chunk := f.read(4096):
process(chunk)
Other notable modern Python additions:
- f-string debugging (3.8): f"{value=}" prints
value=42
- Positional-only parameters (3.8): / in signature
- ParamSpec and Concatenate (3.10): type-check decorator
signatures
- Exception groups (3.11): ExceptionGroup and
except*
- Type parameter syntax (3.12):
def first[T](lst: list[T]) -> T
11.7 Epilogue
This chapter closed the Python Story with its most advanced features. Taken together,
the eleven chapters provide a comprehensive map of modern Python from basic types to
coroutines, metaclasses, and the newest language syntax. The best next step is to apply
these patterns in real projects.
11.8 References
asyncio — python.org
contextlib — python.org
Descriptor How-To Guide — python.org
PEP 634 — Structural Pattern Matching
PEP 572 — Walrus Operator