繁体   English   中英

为什么map()和列表理解的结果不同?

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

以下测试失败:

#!/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]

它们是不同的,因为生成器表达式和列表comp中的i值都是懒惰地计算的,即在f中调用匿名函数时。
到那时,如果是ti被绑定到最后一个值,即-1。

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

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

现在lambdas带有一个引用i的闭包,但在两种情况下i都绑定为-1,因为这是它被分配给的最后一个值。

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

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

这样,您在创建闭包时强制对i进行求值。

编辑 :生成器表达式和列表推导之间存在一个区别:后者将循环变量泄漏到周围的范围中。

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

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

表达式f = lambda: i相当于:

def f():
    return i

表达式g = lambda i=i: i相当于:

def g(i=i):
    return i

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

生成器表达式是lambda表达式中i name的最近的封闭范围(其中i已定义),因此i在该块中解析:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM