lambda - python 执行( λ λ λ λ ) 函数闭包捕获?

  显示原文与译文双语对照的内容
55 3

最近我开始玩 python,我在闭包工作中遇到了一些特殊的东西。请考虑下面的代码:

adders=[0,1,2,3]
for i in [0,1,2,3]:
 adders[i]=lambda a: i+a
print adders[1](3)

它构建一个简单的函数 array,它接受一个输入并返回由一个数字添加的输入。函数在 for 循环中构造,迭代器 i0 运行到 3对于这些数字,创建一个 lambda 函数,捕获 i 并将它的添加到函数的输入。最后一行使用 3 作为参数调用第二个 lambda 函数。令我惊讶的是,输出是 6

我需要一个 4 我的推理是:在 python 中,一切都是一个对象,因此每个变量都是一个指向它的指针。i 创建 lambda 闭包时,我期望它将指针存储到当前由 i 指向的整数对象。这意味着当 i 分配一个新的整数对象时,它不应该影响先前创建的闭包。很遗憾,在调试器中检查 adders array 表明它确实。所有 lambda 函数都引用 i的最后一个值 3,这导致 adders[1](3) 返回 6

这让我不禁想:

  • 关闭的具体内容是什么?
  • 最优雅的方法是让 lambda 函数在 i 更改它的值的方式下捕获 i的当前值。
时间:原作者:0个回答

146 0

第二个问题已经回答了,但对于你的第一个问题:

关闭捕获的确切内容是什么?

python 中的范围是动态和词汇。闭包将始终记住变量的名称和范围,而不是它所指向的对象。由于示例中的所有函数都是在相同的范围内创建并使用相同的变量名,所以它们总是引用相同的变量。

对于你的其他问题,请注意以下两个方面:

最简洁,但不是严格等价的方法是 Plisson推荐的。创建带有额外参数的lambda,并将参数值的额外默认值设置为要保留的对象。

如果你每次创建lambda时创建一个新的作用域,那么更详细一点,但更小一点的技巧将是:

>>> adders = [0,1,2,3]
>>> for i in [0,1,2,3]:
... adders[i] = (lambda b: lambda a: b + a)(i)
... 
>>> adders[1](3)
4
>>> adders[2](3)
5

这里的作用域是使用一个新的函数( 一个 lambda,为了简洁) 来创建的,它绑定它的参数,并将它的绑定为参数。但是在实际代码中,你很可能会有一个普通的函数而不是lambda来创建新的作用域:

def createAdder(x):
 return lambda y: y + x
adders = [createAdder(i) for i in range(4)]
原作者:
104 5

你可以使用带有默认值的参数强制捕获变量:

>>> for i in [0,1,2,3]:
... adders[i]=lambda a,i=i: i+a # note the dummy parameter with a default value
...
>>> print( adders[1](3) )
4

它的思想是声明一个参数( 巧妙命名的i ) 并给它一个你想要捕获的变量的默认值( i的值)

原作者:
135 3

为了完整,另一个问题的答案是:你可以在functools插件模块中使用部分

从运算符中导入 add Lutz proposed示例变成:

from functools import partial
from operator import add # add(a, b) -- Same as a + b.
adders = [0,1,2,3]
for i in [0,1,2,3]:
 # store callable object with first argument given as (current) i
 adders[i] = partial(add, i) 
print adders[1](3)
原作者:
...