Recently I started playing around with Python and I came around something peculiar in the way closures work. Consider the following code:
adders = [None, None, None, None]
for i in [0, 1, 2, 3]:
adders[i] = lambda a: i+a
print adders[1](3)
It builds a simple array of functions that take a single input and return that input added by a number. The functions are constructed in for
loop where the iterator i
runs from 0
to 3
. For each of these numbers a lambda
function is created which captures i
and adds it to the function's input. The last line calls the second lambda
function with 3
as a parameter. To my surprise the output was 6
.
I expected a 4
. My reasoning was: in Python everything is an object and thus every variable is essential a pointer to it. When creating the lambda
closures for i
, I expected it to store a pointer to the integer object currently pointed to by i
. That means that when i
assigned a new integer object it shouldn't effect the previously created closures. Sadly, inspecting the adders
array within a debugger shows that it does. All lambda
functions refer to the last value of i
, 3
, which results in adders[1](3)
returning 6
.
Which make me wonder about the following:
lambda
functions to capture the current value of i
in a way that will not be affected when i
changes its value?For a more accessible, practical version of the question, specific to the case where a loop (or list comprehension, generator expression etc.) is used, see Creating functions (or lambdas) in a loop (or comprehension). This question is focused on understanding the underlying behaviour of the code in Python.
If you got here trying to fix a problem with making buttons in Tkinter, try tkinter creating buttons in for loop passing command arguments for more specific advice.
See What exactly is contained within a obj.__closure__? for technical details of how Python implements closures. See What is the difference between Early and Late Binding? for related terminology discussion.
What do the closures capture exactly?
Closures in Python use lexical scoping: they remember the name and scope of the closed-over variable where it is created. However, they are still late binding: the name is looked up when the code in the closure is used, not when the closure is created. Since all the functions in your example are created in the same scope and use the same variable name, they always refer to the same variable.
There are at least two ways to get early binding instead:
The most concise, but not strictly equivalent way is the one recommended by Adrien Plisson. Create a lambda with an extra argument, and set the extra argument's default value to the object you want preserved.
More verbosely but also more robustly, we can create a new scope for each created lambda:
>>> adders = [0,1,2,3]
>>> for i in [0,1,2,3]:
... adders[i] = (lambda b: lambda a: b + a)(i)
...
>>> adders[1](3)
4
>>> adders[2](3)
5
The scope here is created using a new function (another lambda, for brevity), which binds its argument, and passing the value you want to bind as the argument. In real code, though, you most likely will have an ordinary function instead of the lambda to create the new scope:
def createAdder(x):
return lambda y: y + x
adders = [createAdder(i) for i in range(4)]