提问人:jfs 提问时间:9/26/2008 最后编辑:jfs 更新时间:10/25/2008 访问量:2702
为什么 map() 和列表推导的结果不同?[复制]
Why results of map() and list comprehension are different? [duplicate]
问:
以下测试失败:
#!/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]
答:
6赞
Brian
9/26/2008
#1
lambda 捕获变量,而不是值,因此代码
lambda : i
将始终返回 I 当前在闭包中绑定到的值。在调用它时,此值已设置为 -1。
要获得所需的内容,您需要在创建 lambda 时通过以下方式捕获实际绑定:
>>> 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]
9赞
Torsten Marek
9/26/2008
#2
它们是不同的,因为生成器表达式和列表补偿中的值都是延迟计算的,即当匿名函数在 中调用时。
到那时,绑定到最后一个值 if ,即 -1。i
f
i
t
所以基本上,这就是列表推导的作用(同样适用于 genexp):
x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)
现在,lambda 携带一个引用 的闭包,但在这两种情况下都绑定到 -1,因为这是它被分配的最后一个值。i
i
如果要确保 lambda 接收当前值 ,请执行i
f(*[lambda u=i: u for i in t])
这样,就可以强制在创建闭包时进行评估。i
编辑:生成器表达式和列表推导式之间有一个区别:后者将循环变量泄漏到周围的作用域中。
评论
0赞
S.Lott
9/27/2008
Lambda 是邪恶的,因为它不清楚运行时上下文到底是什么。
4赞
jfs
10/28/2008
@S.Lott:Python 中的普通函数并没有什么不同。 无论考虑函数还是 lambda,您都不知道到底是什么。def f(): return i
i
1赞
unutbu
2/2/2015
在 Python3 中,“循环控制变量不再泄漏到周围的作用域中。
4赞
jfs
9/27/2008
#3
表达式等效于:f = lambda: i
def f():
return i
表达式等效于:g = lambda i=i: i
def g(i=i):
return i
i
在第一种情况下是一个自由变量,在第二种情况下它绑定到函数参数,即在这种情况下,它是一个局部变量。默认参数的值在定义函数时计算。
生成器表达式是表达式中名称的最接近的封闭范围(定义的位置),因此在该块中解析:i
i
lambda
i
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i
是块的局部变量,因此它引用的对象在该块中定义:lambda i: ...
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
评论
[-1, -1]
lambda i: ...