Coroutine = generator + protocol
Under the hood, `async def` returns a coroutine object; `await` is `yield from` with extra rules.
import asyncio
async def f():
return 42
asyncio.iscoroutinefunction(f) # TrueSenior-level depth from the Senior Deep-Dives track. Stuff that separates a mid from a senior in code review and architecture discussion.
Under the hood, `async def` returns a coroutine object; `await` is `yield from` with extra rules.
import asyncio
async def f():
return 42
asyncio.iscoroutinefunction(f) # TrueSingle-threaded scheduler. CPU-bound work blocks it.
# Bad: time.sleep(5) in async def — freezes the loop # Good: await asyncio.sleep(5)
`gather` returns results; `wait` returns (done, pending) sets — more control.
results = await asyncio.gather(a(), b(), c()) # vs done, pending = await asyncio.wait([t1, t2], return_when=asyncio.FIRST_COMPLETED)
Modern replacement for `gather` — structured concurrency, exceptions handled cleanly.
async with asyncio.TaskGroup() as tg:
tg.create_task(work_a())
tg.create_task(work_b())Protects CPython internals — only one thread runs Python bytecode at a time.
# I/O-bound: threads still help (release GIL during syscalls) # CPU-bound: use multiprocessing or 3.13+ free-threading
Cheap, shared memory, fine for parallel HTTP calls.
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(8) as ex:
results = list(ex.map(fetch, urls))Bypasses GIL — each process has its own interpreter.
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(4) as ex:
results = list(ex.map(heavy_compute, chunks))Experimental no-GIL build. Genuine threaded parallelism for CPU work.
python3.13t -X gil=0 script.py
Powers `@property`, `staticmethod`, ORM fields. `__get__`/`__set__`/`__delete__`.
class Validated:
def __set_name__(self, owner, name): self.name = name
def __get__(self, inst, owner): return inst.__dict__[self.name]
def __set__(self, inst, value):
if value < 0: raise ValueError
inst.__dict__[self.name] = valueUse sparingly. Most cases solved by `__init_subclass__`.
class Tracked:
_all = []
def __init_subclass__(cls, **kw):
super().__init_subclass__(**kw)
Tracked._all.append(cls)Save memory by skipping __dict__ per instance.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y): self.x, self.y = x, yFunction-level timings — find the hot path.
python -m cProfile -o out.pstats script.py python -m pstats out.pstats <<< "sort cumulative\nstats 20"
Heap profiler — find leaks and giant allocations.
memray run script.py memray flamegraph memray-script.bin
Replace hand-rolled dict caches.
from functools import cache @cache def fib(n): return n if n < 2 else fib(n-1) + fib(n-2)
C loop > Python loop for numeric work.
np.where(arr > 0, arr * 2, 0) # vs a Python for loop
Capture the why of every load-bearing decision.
# ADR-0042: Use Redis Streams over Kafka # Status: accepted # Context: … # Decision: … # Consequences: + / -
What a senior actually checks.
# 1. Does it have to exist? # 2. Is the failure mode obvious? # 3. Will it page someone at 3am? # 4. What's the test that proves it works?
Want to actually learn these patterns, not just paste them? Open the Senior Python cheatsheet track — each snippet has a full lesson behind it.