Decorators & Closures Mechanics
Master closure cell manipulation, decorator stacking, memory retention, functools internals, and advanced function wrapping patterns.
Theoretical Mechanics & Memory Architecture
In Python, functions are first-class citizen objects capable of being bound to identifiers, passed as parameters, and returned from higher-order functions. A closure captures free variables from its enclosing lexical scope using CPython cell objects (PyCellObject). Decorators utilize this metaprogramming primitive to wrap callables, enabling transparent caching, validation, instrumentation, and access control at definition time.
When an inner function references an outer local variable, CPython allocates a PyCellObject on the heap to store the pointer. Both the outer and inner stack frames access the variable via the LOAD_DEREF and STORE_DEREF opcodes rather than LOAD_FAST. Because the cell object resides on the heap, the inner function retains access to the variable even after the outer function frame returns and terminates.
Closures look up free variables when invoked, not when defined. Creating lambdas inside a loop (such as [lambda: i for i in range(5)]) causes all five functions to reference the same cell object containing the loop's final value (4). Fix this by binding the variable into default argument scope: lambda i=i: i. Furthermore, decorators must use @functools.wraps(fn) to prevent overwriting the wrapped function's __name__ and __doc__.
Interactive Dry-Run Execution Workspace
Mutating Closure Variables via __closure__
Trace CPython execution step-by-step and deduce the exact stdout string emitted by this snippet.
def make_adder(x):
def adder(y):
return x + y
return adder
f = make_adder(10)
cell = f.__closure__[0]
cell.cell_contents += 5
print(f(5))Frequently Asked Questions on Decorators & Closures Mechanics
Why do functions created in loops all return the same value when called later?
Python closures bind to variables, not values (late binding). All created functions point to the identical cell object holding the loop variable. When invoked later, they look up the variable's current value, which is whatever the final loop iteration assigned.
How does @functools.wraps preserve function introspection in production code?
Decorating a function replaces it with an inner wrapper function, obscuring the original name, docstring, and annotations. @functools.wraps copies attributes (__name__, __doc__, __module__, __annotations__) from the wrapped callable onto the wrapper.
What is the execution order when multiple decorators are applied to one function?
Decorators are applied from the bottom up (inside out) at function definition time, but execute from the top down (outside in) when the decorated function is invoked.