python - lambda 函数闭包捕获什么?

标签 python lambda closures

最近我开始使用 Python,并且发现了闭包工作方式中的一些奇特之处。考虑以下代码:

adders=[None, None, None, None]

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

print adders[1](3)

它构建了一个简单的函数数组,该数组接受单个输入并返回该输入加上一个数字。这些函数在 for 循环中构造,其中迭代器 i0 运行到 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/

相关文章:

php - 命名实体识别算法

amazon-web-services - 在 EC2 机器启动时获取用户数据

c++ - Qt 信号 lambda 导致 shared_ptr 泄漏?

javascript - 单击返回按钮 ID - JavaScript

groovy - 为什么 Groovy 闭包声明一个最大值,而不是常量数量的参数

python - 如何通过拆分 python 中的列表元素来创建列表?

python - Pandas - 从系列的开头删除数字

Java8 实例化谓词

PHP闭包变量范围

python - 如何从 Django 迁移中调用基于实例的函数并访问实例变量