Question

Why this code

>>> i=1
>>> add_one=lambda x:x+i
>>> my_list=[add_one(i) for i in [0,1,2]]

in Python 2.7 throws this result:

>>> my_list
[0, 2, 4]

But the same code in Python 3.3 throws this other result:

>>> my_list
[1, 2, 3]

It's more sense for me the result with Python 2.7, because the 'i' variable is in the same name scope where the lambda function is called from. I don't understand these unequal behaviors of the lambdas functions in the two branches of Python.

Was it helpful?

Solution

The difference is that in Python 3, list comprehensions do not leak their variable into the enclosing scope. You can see some discussion of this by Guido here; that post includes an example very similar to yours.

In both versions, your add_one(i) is a function referencing a variable i. This variable will be looked up by name in the enclosing scope at the time the function is called. Since the function is defined at the global module level, "the enclosing scope" is the global scope. That means that when you call add_one it will use whatever value of i it finds in the global scope (i.e., it will look for a global variable called i).

In Python 2, the list comprehension leaks its variable i to the enclosing scope. Since your list comprehension is at the global module-level scope, "the enclosing scope" is again global, and the leaked variable i is a global variable, overwriting the value you initially set with your i = 1 line. Calling add_one then references this variable. Since i changes with each iteration, your function effectively does i+i on each iteration.

In Python 3, the list comprehension creates its own variable in a private scope. But your function still accesses the global i. Since the Python 3 list comprehension doesn't leak to the global scope, this global i never changes, and your function effectively does x+1 on each iteration (where x is the loop variable of the list comprehension).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top