Functions & Closures
A track of P29 · Functions, Decorators & Generators.
Functions as values: capture state in closures, rebind it with nonlocal, and build function factories, composition and partial application - the machinery every decorator and callback idiom stands on.
Run this in your head: fs = [lambda: i for i in range(3)] — three little
functions built in a loop. Now call them. Every single one returns 2. Not
0, 1, 2 — three twos. If that surprises you, you have just met the gap this
track closes: what a closure actually captures, and when it looks. If it does
not surprise you, the follow-up usually does: name two fixes without running
the code.
The track starts one floor below the trap: in Python a def produces a value
— an object you can store, pass to sorted(key=...), and return from another
function. That last move is where closures appear: an inner function that
refers to its maker's variables keeps them alive after the maker returns. The
catch — and the lambda-loop trap above — is that it captures the variable,
a live cell, not a snapshot of the value; the lookup happens at call time.
Add nonlocal and the inner function can also rebind that state, which
turns a two-line factory into a counter, an accumulator, a rate limiter —
small stateful tools with no class in sight.
The payoff module then puts the pattern to work: composition (f(g(x)) as a
returned function) and partial application (freeze some arguments now, supply
the rest later) — both rebuilt by hand rather than imported, so
functools.partial stops being magic. Everything downstream in this pillar
leans on this track: a decorator is nothing but a closure over the function it
wraps, and P29's decorators track opens exactly there.