最近我开始摆弄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)
返回lambda
0。
这让我想知道以下几点:
lambda
函数捕获i
的当前值而不受i
改变其值的影响的最优雅的方法是什么?对于这个问题更容易理解、更实用的版本,具体到使用循环(或列表推导式、生成器表达式等)的情况,请参见在循环(或推导式)中创建函数(或lambdas) 。这个问题的重点是理解Python中代码的底层行为。
如果您在这里试图修复在Tkinter中制作按钮的问题,请尝试 Tkinter在for循环中创建按钮,传递命令参数以获得更具体的建议。
见 obj.__closure__中到底包含什么?为如何 Python实现闭包的技术细节。见早期绑定和后期绑定的区别是什么?用于相关术语讨论。