为什么 map() 和列表推导的结果不同?[复制]

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

提问人:jfs 提问时间:9/26/2008 最后编辑:jfs 更新时间:10/25/2008 访问量:2702

问:

以下测试失败:

#!/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]
Python 闭包 list-comprehension 后期绑定 生成器-表达式

评论

3赞 Beni Cherniavsky-Paskin 1/8/2010
对于像我这样的人,请阅读问题,但一开始没有注意到任何问题:请注意!本质上,在循环中不会捕获 i 的当前值。[-1, -1]lambda i: ...
0赞 jfs 10/5/2016
相关 来自 Python 常见问题解答:为什么在具有不同值的循环中定义的 lambda 都返回相同的结果?

答:

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。
ifit

所以基本上,这就是列表推导的作用(同样适用于 genexp):

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

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

如果要确保 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 ii
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在第一种情况下是一个自由变量,在第二种情况下它绑定到函数参数,即在这种情况下,它是一个局部变量。默认参数的值在定义函数时计算。

生成器表达式是表达式中名称的最接近的封闭范围(定义的位置),因此在该块中解析:iilambdai

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

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

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