Louis Abraham's Home Page

On Python's lambda and closures

25 Jul 2017


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)

EDIT (16/01/21): my friend Jules suggested me yet another way that I think is elegant.

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

Basically, the lambda function takes a second argument that is provided with a default value. Thus, since the left-hand side i is a function argument, it is treated as a local variable. To be more explicit to the reader, the following example works the same:

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

I like this method because it reminds me of lambda captures in C++.