6

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.

1 Answer 1

13

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).

Sign up to request clarification or add additional context in comments.

2 Comments

In a first moment I'd think that the lambda function takes the value of 'i' when the function is declared, instead that when the function is called. But then I tried this: >>> i=1 >>> add_one=lambda x:x+i >>> i=5 >>> my_list=[add_one(i) for i in [0,1,2]] and the 'my_list' threw: [5, 6, 7]. Then I was astonished with the behavior of lambda function in Python3. Your explanation of the scope of the lists compehensions clarifies everything. In GvR words: the variables in the list comprehension temporarily shadows but does not override the the same name variables in the surrounding scope.
@Trimax: People are often confused by the way functions close over variables. But it essentially has to work this way. If functions took the value of the variable at the time they were defined, it wouldn't be possible to write a function that used an ordinary global variable in the ordinary way (i.e., looking it up at call time).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.