In Python, why do lambdas in list comprehensions overwrite themselves in retrospect?
Question:
Consider the following code:
def f (a):
return 1
def g (a):
return 2
[lambda a: i(a) for i in (f,g)][0](0)
The result is 2. Where it should clearly be 1! I would expect this code to execute as follows. Create a list. Copy the function f into the first index. Copy g into the second index. Fin.
Why does this execute the way it does?!
Answers:
The lambda didn’t overwrite itself, it is i
that was overwritten. This is a common mistake regarding variable scopes. Try this:
[lambda a, i=i: i(a) for i in (f,g)][0](0)
(the difference is binding the value of i
at the time the lambda is created)
See also:
I’ll expand slightly on @dsh’s answer.
When the list comprehension expression
[lambda a: i(a) for i in (f,g)]
is evaluated, it creates two anonymous functions, each of which to the effect of
def _______(a):
return i(a)
However, after the evaluation of the above list comprehension, the name i
is bound to its last iteration value, which is the object bound to g
, i.e. 2nd of your functions.
Now, after that, when one of the anonymous functions in the list gets called, it accesses the name i
, which is in the module-global scope and bound to the same thing as g
is.
Edit: oh, @dsh’s expanded it too.
Consider the following code:
def f (a):
return 1
def g (a):
return 2
[lambda a: i(a) for i in (f,g)][0](0)
The result is 2. Where it should clearly be 1! I would expect this code to execute as follows. Create a list. Copy the function f into the first index. Copy g into the second index. Fin.
Why does this execute the way it does?!
The lambda didn’t overwrite itself, it is i
that was overwritten. This is a common mistake regarding variable scopes. Try this:
[lambda a, i=i: i(a) for i in (f,g)][0](0)
(the difference is binding the value of i
at the time the lambda is created)
See also:
I’ll expand slightly on @dsh’s answer.
When the list comprehension expression
[lambda a: i(a) for i in (f,g)]
is evaluated, it creates two anonymous functions, each of which to the effect of
def _______(a):
return i(a)
However, after the evaluation of the above list comprehension, the name i
is bound to its last iteration value, which is the object bound to g
, i.e. 2nd of your functions.
Now, after that, when one of the anonymous functions in the list gets called, it accesses the name i
, which is in the module-global scope and bound to the same thing as g
is.
Edit: oh, @dsh’s expanded it too.