Home Lambda madness
Post
Cancel

Lambda madness

A little while back, I have encountered something rather interesting with regards to Python’s lambda. I searched around online and found other people with similar issues such as this one What do (lambda) function closures capture? Using the example posed by the thread starter:

1
2
3
4
adders = [0, 1, 2, 3]
for i in [0, 1, 2, 3]:
    adders[i] = lambda a: i+a
print adders[1](3)

So what this is supposed to be doing is replacing the adders list with a little mini lambda function. So what we are expecting should be

1
2
3
4
adders[0] = [0+a]
adders[1] = [1+a]
adders[2] = [2+a]
adders[3] = [3+a]

But as the poster have said, that would mean adders[1](3) would mean 1+3=4. However, to everybody’s surprise, it returned a 6, which would indicate that it is actually getting it from adders[3] with a lambda of 3+3=6. It would appear that Python lambda when going through a loop like that, i is referencing the last value of the loop. Since all the variables are using the same name and within the same scope, we need to find a way to break that out. There are two easy way to do that as suggested by posters in the thread.

1
2
3
4
adders = [0,1,2,3]
for i in [0,1,2,3]:
    adders[i]=lambda a,i=i: i+a
print adders[1](3)

This method will force the lambda to capture the i as part of its expression. This should separate the scope of i in the lambda expression to itself.

1
2
3
4
adders = [0,1,2,3]
for i in [0,1,2,3]:
    adders[i] = (lambda b: lambda a: b + a)(i)
print adders[1](3)

This method as well, in its own way, is also separating out the scopes so it’s localized to the lambda expression itself.

This is very handy to know because I have had situations where I am procedurally generating buttons for a GUI that triggers procedurally generated function via a loop. Ultimately all the buttons point to the same function, which is the last function in the loop list. That caused a lot of confusion!

This post is licensed under CC BY 4.0 by the author.