最近我开始尝试使用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的当前值,最优雅的方法是什么?
如果您想了解在Python中循环(或列表推导式、生成器表达式等)中创建函数(或lambda)的情况下更易于理解和实用的问题,请参见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?以进行相关术语讨论。
i
如何离开命名空间? - detly