9

以下测试失败:为什么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] 
+3

对于像我这样的读者来说,最初没有注意到任何问题:注意`[ - 1,-1]`!本质上`lambda i:...`在循环中不捕获i的当前值。与Python相关的 – 2010-01-07 17:27:13

+0

常见问题解答:[为什么使用不同值的循环中定义的lambda都返回相同的结果?](https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined -in-a-loop-with-different-values-all-return-the-same-result) – jfs 2016-10-05 15:11:02

回答

9

他们是不同的,因为i两个发电机表达式的值和列表comp懒惰地评估,即当在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在这两种情况下,因为这是它分配给的最后一个值。

如果你想确保拉姆达接收的i当前值,做

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

这样,你强制i评价在创建关闭的时间。

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

5

拉姆达捕获变量,不值,因此代码

lambda : i 

将始终返回i的值目前必然在关闭。当它被调用的时候,这个值已经被设置为-1。

为了得到你想要的东西,你需要获得实际的结合在拉姆达创建时,借:

>>> 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] 
3

表达f = lambda: i等效于:

def f(): 
    return i 

表达g = lambda i=i: i等效于:

def g(i=i): 
    return i 

i是在第一种情况下一个free variable并且它被绑定到的函数参数在第二种情况下即在这种情况下它是一个局部变量。默认参数的值在函数定义时进行评估。

发生器表达是最近的封闭范围(其中i被定义),用于i名称在lambda表达,因此i是在该块中解决的问题:

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

ilambda i: ...块的局部变量,因此它所指的对象在该块中被定义为:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1] 
相关问题