Warming up the neural circuits...
By the end of this chapter you will:
Many "mysterious" Python bugs are scope bugs.
If you can reason clearly about variable lookup and capture, your functions become easier to test, safer to reuse, and faster to refactor.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
return x
return inner()
print(outer()) # local
print(x) # globalLookup order: Local -> Enclosing -> Global -> Builtins.
def make_multiplier(factor):
def multiply(value):
return value * factor
return multiply
triple = make_multiplier(3)
print(triple(10)) # 30Closures capture variables by reference to the enclosing scope.
def make_counter():
count = 0
def next_value():
nonlocal count
count += 1
return count
return next_value
counter = make_counter()
print(counter()) # 1
print(counter()) # 2map for simple projections when readability is strong.filter for predicate-based selection.functools.partial for reusable preconfigured call signatures.from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
print(square(7)) # 49| Mistake | Why it hurts | Better move |
|---|---|---|
| Assuming assignment updates outer variable | Creates new local binding | Use nonlocal intentionally |
| Overusing lambda chains | Hard to debug and test | Use named functions |
| Hidden closure state | Surprising side effects | Document state transitions clearly |
| Using globals for shared state | Tight coupling, test fragility | Prefer closure/class dependency injection |
A nested function with repeated variable namesCorrect values based on LEGB lookup orderfactor = 5multiplier(10) returns 50counter() called three times1, 2, 3A chained map/filter expressionEquivalent readable implementation with named helpersYou need configurable validation logic with future extensionExplain when closure is enough and when class is betterBeginner:
"What does LEGB stand for?"
Local, Enclosing, Global, Builtins. It is Python's variable lookup order.
"What problem do closures solve?"
Closures let you create functions with captured configuration/state without needing a class.
Senior:
"When is nonlocal a good idea, and when is it a smell?"
It is good for small, explicit state machines. It is a smell when state grows complex and deserves class-based structure.
"How do you decide between functional style and imperative loops?"
Prioritize readability and maintainability. Functional helpers are great for simple pipelines; explicit loops win for complex branching.
nonlocal is powerful but should be explicit and constrained.Need config + reusable behavior? -> closure
Need state in function? -> closure + nonlocal
Need complex logic? -> explicit named function or loopIn LEGB lookup, what comes immediately after Local?
Use nonlocal only when shared mutable is intentional and explicit.