Closures & nonlocal
A closure is a function that remembers the values from its enclosing scope even after that scope has finished executing. Understanding closures is key to mastering decorators, callbacks, and any pattern that requires stateful functions without a class.
What is a Closure?
When a function is defined inside another function and references variables from the outer function, Python captures those variables. The inner function + the captured environment together form a closure.
The variable greeting is called a free variable โ it's used inside greet but defined in the enclosing scope.
Inspecting Closures
Python stores captured variables in cell objects accessible via __closure__:
The nonlocal Keyword
By default, assignment inside an inner function creates a new local variable โ it does not modify the outer variable. The nonlocal keyword explicitly tells Python "this name belongs to the enclosing scope."
Without nonlocal, count += 1 would raise UnboundLocalError because Python would interpret count as a local variable that is read before assignment.
Practical Example: make_adder
Closures are excellent for factories โ functions that return customised functions:
Simple Memoization with a Closure
You can build a lightweight cache without importing functools:
Here cache is a free variable shared by all calls to wrapper. It persists between calls because the closure keeps it alive.
The Classic Late-Binding Gotcha
This is one of the trickiest Python bugs involving closures. When a closure references a loop variable, all the generated closures end up sharing the same reference โ the final value of the loop variable.
The reason is late binding: Python closures look up free variable names in the enclosing scope at the time the closure is called, not when it is defined. By the time any of the lambdas run, the loop has finished and i is 4.
Closures vs Classes
A closure is essentially a lightweight object with a single method. When you have only one behaviour, a closure is often more concise:
Use a closure when you have one or two closely related inner functions and minimal state. Use a class when you need multiple methods, inheritance, or the state grows complex.
Knowledge Check
What is a 'free variable' in the context of Python closures?
Why does this code print '4 4 4 4 4' instead of '0 1 2 3 4'? funcs = [lambda: i for i in range(5)]
What does the nonlocal keyword do?