为什么map()和列表理解的结果不同?
以下测试失败:
#!/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]
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]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
它们是不同的,因为生成器表达式和列表 comp 中的
i
值都是延迟计算的,即在f
中调用匿名函数时。到那时,
i
绑定到最后一个值 ift
,即 -1。基本上,这就是列表推导式的作用(对于 genexp 也是如此):
现在 lambda 携带一个引用
i
的闭包,但是i
在中绑定到 -1两种情况,因为这是分配给它的最后一个值。如果您想确保 lambda 接收
i
的当前值,请这样做,您可以在创建闭包时强制对
i
求值。编辑:生成器表达式和列表推导式之间有一个区别:后者将循环变量泄漏到周围的范围中。
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 inf
.By that time,
i
is bound to the last value ift
, which is -1.So basically, this is what the list comprehension does (likewise for the genexp):
Now the lambdas carry around a closure that references
i
, buti
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
, doThis 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.
lambda 捕获变量,而不是值,因此代码
将始终返回闭包中当前绑定到的值 i。 当它被调用时,该值已被设置为-1。
为了得到你想要的,你需要在创建 lambda 时捕获实际的绑定,方法是:
The lambda captures variables, not values, hence the code
will always return the value i is currently bound to in the closure. By the time it gets called, this value has been set to -1.
To get what you want, you'll need to capture the actual binding at the time the lambda is created, by:
表达式
f = lambda: i
等效于:表达式
g = lambda i=i: i
等效于:i
是一个 自由变量 在第一种情况下,它绑定到第二种情况下的函数参数,即,它在这种情况下是一个局部变量。 默认参数的值在函数定义时评估。生成器表达式是
lambda
表达式中i
名称最近的封闭范围(其中定义了i
),因此i
> 在该块中解析:i
是lambda i: ...
块的局部变量,因此它引用的对象是在该块中定义的:Expression
f = lambda: i
is equivalent to:Expression
g = lambda i=i: i
is equivalent to:i
is a free variable in the first case and it is bound to the function parameter in the second case i.e., it is a local variable in that case. Values for default parameters are evaluated at the time of function definition.Generator expression is the nearest enclosing scope (where
i
is defined) fori
name in thelambda
expression, thereforei
is resolved in that block:i
is a local variable of thelambda i: ...
block, therefore the object it refers to is defined in that block: