12

以下测试失败:

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

3 回答 3

9

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

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

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

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

如果要确保 lambda 接收 的当前值i,请执行

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

这样,您可以i在创建闭包时强制进行评估。

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

于 2008-09-26T14:31:47.577 回答
6

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]
于 2008-09-26T14:28:40.580 回答
4

表达式f = lambda: i等价于:

def f():
    return i

表达式g = lambda i=i: i等价于:

def g(i=i):
    return i

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

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

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

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

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
于 2008-09-26T18:24:31.977 回答