Louis Abraham's Home Page

/ On Python's lambda and closures


First, a little Python riddle.

What is the value of this?

[lambda x: x + i + 1
 for i in range(24, 42)][0](0)

Do you think the answer is 25? Any sane person would.

Well, you are wrong. Obviously the answer is 42, as always. Wait . . . wat?

For the moment, let’s do almost the same in a simpler way:

l = []
for i in range(24, 42):
	l.append(lambda x: x + i + 1)
	print(l[0](0))

And it prints the numbers from 25 to 42. Now, try:

>>> i = 4242
>>> print(l[0](0))
4242

Now it is obvious: the lambda uses the value of i of the scope where it is defined. You can do funny things by creating the lambda in the scope of a function with a local, nonlocal or global i.

Let’s go back to our problem:

>>> [lambda x: x + i + 1 for i in range(24, 42)][0]
<function <listcomp>.<lambda>>

The <listcomp> means that the list comprehension defines its own closure. And in this closure, all the lambda functions take the same value of i as it changes. Note that if you use Python 2, the variable i is even accessible from the global context as the list comprehension does not define any closure (but generator expressions do, so it’ll work with tuples, sets or dictionaries).

Now, you have some solutions depending on the type of user you are.

from functools import partial
from operator import add

[partial(add, i + 1) for i in range(24, 42)][0](0)
[(lambda i: lambda x: x + i + 1)(i)
 for i in range(24, 42)][0](0)
[eval('lambda x: x + i + 1', {'i': i})
 for i in range(24, 42)][0](0)