【发布时间】:2021-08-04 04:42:59
【问题描述】:
最近我开始使用 Python,并且发现了闭包工作方式中的一些特殊情况。考虑以下代码:
adders=[None, None, None, None]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
它构建了一个简单的函数数组,这些函数接受单个输入并返回该输入加上一个数字。这些函数在for 循环中构造,其中迭代器i 从0 运行到3。对于这些数字中的每一个,都会创建一个 lambda 函数,该函数会捕获 i 并将其添加到函数的输入中。最后一行以3 作为参数调用第二个lambda 函数。令我惊讶的是,输出是6。
我期待一个4。我的理由是:在 Python 中,一切都是对象,因此每个变量都是指向它的指针。在为i 创建lambda 闭包时,我希望它存储一个指向当前由i 指向的整数对象的指针。这意味着当i 分配一个新的整数对象时,它不应该影响之前创建的闭包。可悲的是,在调试器中检查 adders 数组表明确实如此。所有lambda函数都引用i的最后一个值3,这导致adders[1](3)返回6。
这让我想知道以下几点:
- 闭包究竟捕获了什么?
- 说服
lambda函数以在i更改其值时不会受到影响的方式捕获i的当前值的最优雅方法是什么?
【问题讨论】:
-
我在 UI 代码中遇到过这个问题。把我逼疯了。诀窍是记住循环不会创建新范围。
-
@TimMB
i如何离开命名空间? -
@detly 好吧,我想说
print i在循环后不起作用。但我为自己测试了它,现在我明白你的意思了——它确实有效。我不知道循环变量在python中的循环体之后徘徊。 -
这在官方 Python 常见问题解答中,在 Why do lambdas defined in a loop with different values all return the same result? 下,有解释和通常的解决方法。
-
@abarnert:所以在 C++ 中,带有
[&]的 lambda 是闭包(尽管生命周期有限),而带有[=]的 lambda 不是闭包?这种定义的选择不会让任何人感到困惑 ;-) 在nonlocal之前的 Python 中,赋值 没有 对嵌套函数中捕获的变量起作用,所以它们是闭包还是被读取- 仅通过引用足以成为闭包的词法范围进行绑定?我想知道以这种方式将 lambda 演算应用于命令式语言是否实际上是在浪费精力,最好是发明新的术语......