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?you may force the capture of a variable using an argument with a default value:
>>> for i in [0,1,2,3]:
... adders[i]=lambda a,i=i: i+a # note the dummy parameter with a default value
...
>>> print( adders[1](3) )
4
the idea is to declare a parameter (cleverly named i
) and give it a default value of the variable you want to capture (the value of i
)