Python Story

Chapter #11 – Interesting

async/await, metaclasses, descriptors, context managers, pattern matching

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