The term "closure" was borrowed from functional programming and appeared in Python from the very beginning. Closures allow a function to remember the environment in which it was created, even when called outside that environment. This concept provides flexibility and enables the implementation of many patterns, including function factories and lazy computations.
In Python, functions are first-class objects. Sometimes, it is required for the inner function to use variables from the enclosing function's scope even after it has completed. Regular lexical scoping does not guarantee this upon function return. If such a function refers to the environment variables of its creation — a closure occurs.
A closure arises if the inner function refers to variables defined in the outer one, and that outer function returned the inner one to the outside. This is often used to create function factories, encapsulate state without classes, and construct functions with parameters "on the spot".
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30
nonlocal keyword is used.Can a closure maintain mutable state between calls if the variable is modified within the inner function?
Yes, if the nonlocal keyword is used within the inner function. Without nonlocal, the assignment creates a new local variable, not modifying the outer one.
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2
Can private variables be implemented in Python using closures instead of classes?
Yes, closures provide a simple implementation of "private" variables that are inaccessible from the outside unless getters/setters are provided in the inner function.
Are closures only applicable to functions? Can closures be organized with lambdas in Python?
Yes, a closure can also form with lambda expressions, as they are similar to def in terms of lexical variable binding.
def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8
nonlocal.A function factory forming handlers in a loop uses the loop variable inside a closure:
handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]
Pros:
Cons:
i, its last value 2 — behavior is unexpected for most.Used a default argument to "fix" the value:
handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]
Pros:
Cons: