最近我开始使用 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
时不会受到影响code> 改变它的值?
有关问题的更易于理解、更实用的版本,特定于使用循环(或列表理解、生成器表达式等)的情况,请参阅 Creating functions (or lambdas) in a loop (or comprehension) 。这个问题的重点是理解Python代码的底层行为。
如果您尝试解决在 Tkinter 中制作按钮的问题,请尝试 tkinter creating buttons in for loop passing command arguments获取更具体的建议。
参见What exactly is contained within a obj.__closure__?了解Python如何实现闭包的技术细节。请参阅What is the difference between Early and Late Binding?进行相关术语讨论。
最佳答案
您可以使用具有默认值的参数强制捕获变量:
>>> 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
的值)
关于python - lambda 函数闭包捕获什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53680833/