Why results of map() and list comprehension are different?

closures, generator-expression, late-binding, list-comprehension, python

Solution

They are different, because the value of `i` in both the generator expression and the list comp are evaluated lazily, i.e. when the anonymous functions are invoked in `f`. By that time, `i` is bound to the last value if `t`, which is -1.

So basically, this is what the list comprehension does (likewise for the genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Now the lambdas carry around a closure that references `i`, but `i` is bound to -1 in both cases, because that is the last value it was assigned to.

If you want to make sure that the lambda receives the current value of `i`, do

f(*[lambda u=i: u for i in t])

This way, you force the evaluation of `i` at the time the closure is created.

Edit: There is one difference between generator expressions and list comprehensions: the latter leak the loop variable into the surrounding scope.

Problem

The following test fails: ``` #!/usr/bin/env python def f(*args): """ >>> t = 1, -1 >>> f(*map(lambda i: lambda: i, t)) [1, -1] >>> f(*(lambda: i for i in t)) # -> [-1, -1] [1, -1] >>> f(*[lambda: i for i in t]) # -> [-1, -1] [1, -1] """ alist = [a() for a in args] print(alist) if __name__ == '__main__': import doctest; doctest.testmod() ``` In other words: ``` >>> t = 1, -1 >>> args = [] >>> for i in t: ... args.append(lambda: i) ... >>> map(lambda a: a(), args) [-1, -1] >>> args = [] >>> for i in t: ... args.append((lambda i: lambda: i)(i)) ... >>> map(lambda a: a(), args) [1, -1] >>> args = [] >>> for i in t: ... args.append(lambda i=i: i) ... >>> map(lambda a: a(), args) [1, -1] ```

Original source

Related problems