为什么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式是在lambda
expression式中i
名字最近的封闭范围( i
定义的地方),因此i
在该块中parsing:
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i
是lambda i: ...
块的局部variables,因此它引用的对象在该块中定义:
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]