为什么map()和list的理解结果不一样?

以下testing失败:

#!/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() 

换一种说法:

 >>> 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] 

它们是不同的,因为在生成器expression式和列表组件中, i的值是懒惰地评估的,也就是说当匿名函数在f被调用时。
那个时候,如果t是-1,那i就是最后一个值。

所以基本上,这就是列表理解所做的(对于genexp也是如此):

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

现在,lambda带有一个引用i的闭包,但是在两种情况下, i都是-1,因为这是最后一个赋值。

如果你想确保lambda接收到i的当前值,那么

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

通过这种方式,您可以在创buildclosures时强制对i进行评估。

编辑 :生成器expression式和列表parsing之间有一个区别:后者将循环variables泄漏到周围的范围。

lambda捕获variables,而不是值,因此代码

 lambda : i 

将始终返回我目前在封闭中绑定的值。 当它被调用的时候,这个值已经被设置为-1。

为了得到你想要的,你需要在lambda创build时捕获实际的绑定,方法是:

 >>> f(*(lambda i=i: i for i in t)) # -> [-1, -1] [1, -1] >>> f(*[lambda i=i: i for i in t]) # -> [-1, -1] [1, -1] 

expression式f = lambda: i相当于:

 def f(): return i 

expression式g = lambda i=i: i相当于:

 def g(i=i): return i 

在第一种情况下, i是一个自由variables ,它在第二种情况下绑定到函数参数,即在这种情况下它是一个局部variables。 默认参数的值在函数定义时进行评估。

生成器expression式是在lambdaexpression式中i名字最近的封闭范围( i定义的地方),因此i在该块中parsing:

 f(*(lambda: i for i in (1, -1)) # -> [-1, -1] 

ilambda i: ...块的局部variables,因此它引用的对象在该块中定义:

 f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]