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.

Functionsdef makes a valueClosurescapture variables, not valuesnonlocalstate without a classFactoriescompose, partially apply
From function-as-value to functions that build functions.