What do lambda function closures capture?(lambda 函数闭包捕获了什么?)
问题描述
最近我开始使用 Python,并且发现了闭包工作方式中的一些特殊情况.考虑以下代码:
Recently I started playing around with Python and I came around something peculiar in the way closures work. Consider the following code:
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
.
It builds a simple array of functions that take a single input and return that input added by a number. The functions are constructed in for
loop where the iterator i
runs from 0
to 3
. For each of these numbers a lambda
function is created which captures i
and adds it to the function's input. The last line calls the second lambda
function with 3
as a parameter. To my surprise the output was 6
.
我希望是 4
.我的理由是:在 Python 中,一切都是对象,因此每个变量都是指向它的指针.在为 i
创建 lambda
闭包时,我希望它存储一个指向当前由 i
指向的整数对象的指针.这意味着当 i
分配一个新的整数对象时,它不应该影响之前创建的闭包.可悲的是,在调试器中检查 adders
数组表明确实如此.所有 lambda
函数都引用 i
的最后一个值 3
,这导致 adders[1](3)
返回 6
.
I expected a 4
. My reasoning was: in Python everything is an object and thus every variable is essential a pointer to it. When creating the lambda
closures for i
, I expected it to store a pointer to the integer object currently pointed to by i
. That means that when i
assigned a new integer object it shouldn't effect the previously created closures. Sadly, inspecting the adders
array within a debugger shows that it does. All lambda
functions refer to the last value of i
, 3
, which results in adders[1](3)
returning 6
.
这让我想知道以下几点:
Which make me wonder about the following:
- 闭包究竟捕获了什么?
- 说服
lambda
函数以在i
改变它的值?
- What do the closures capture exactly?
- What is the most elegant way to convince the
lambda
functions to capture the current value ofi
in a way that will not be affected wheni
changes its value?
推荐答案
你的第二个问题已经回答了,但是你的第一个问题:
Your second question has been answered, but as for your first:
闭包到底捕获了什么?
Python 中的作用域是动态和 词法的.闭包将永远记住变量的名称和范围,而不是它指向的对象.由于您示例中的所有函数都是在同一范围内创建并使用相同的变量名,因此它们始终引用相同的变量.
Scoping in Python is dynamic and lexical. A closure will always remember the name and scope of the variable, not the object it's pointing to. Since all the functions in your example are created in the same scope and use the same variable name, they always refer to the same variable.
关于如何克服这个问题的其他问题,我想到了两种方法:
Regarding your other question of how to overcome this, there are two ways that come to mind:
最简洁但并非严格等效的方法是Adrien Plisson 推荐的方法.创建一个带有额外参数的 lambda,并将额外参数的默认值设置为您要保留的对象.
The most concise, but not strictly equivalent way is the one recommended by Adrien Plisson. Create a lambda with an extra argument, and set the extra argument's default value to the object you want preserved.
每次创建 lambda 时都创建一个新作用域,这样会更冗长但不那么 hacky:
A little more verbose but less hacky would be to create a new scope each time you create the lambda:
>>> adders = [0,1,2,3]
>>> for i in [0,1,2,3]:
... adders[i] = (lambda b: lambda a: b + a)(i)
...
>>> adders[1](3)
4
>>> adders[2](3)
5
这里的作用域是使用一个新函数(为简洁起见是一个 lambda)创建的,该函数绑定了它的参数,并将您想要绑定的值作为参数传递.但是,在实际代码中,您很可能会使用普通函数而不是 lambda 来创建新作用域:
The scope here is created using a new function (a lambda, for brevity), which binds its argument, and passing the value you want to bind as the argument. In real code, though, you most likely will have an ordinary function instead of the lambda to create the new scope:
def createAdder(x):
return lambda y: y + x
adders = [createAdder(i) for i in range(4)]
这篇关于lambda 函数闭包捕获了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:lambda 函数闭包捕获了什么?
基础教程推荐
- 使用 Google App Engine (Python) 将文件上传到 Google Cloud Storage 2022-01-01
- 使 Python 脚本在 Windows 上运行而不指定“.py";延期 2022-01-01
- 如何在 Python 中检测文件是否为二进制(非文本)文 2022-01-01
- 合并具有多索引的两个数据帧 2022-01-01
- 将 YAML 文件转换为 python dict 2022-01-01
- 哪些 Python 包提供独立的事件系统? 2022-01-01
- 使用Python匹配Stata加权xtil命令的确定方法? 2022-01-01
- 如何在Python中绘制多元函数? 2022-01-01
- Python 的 List 是如何实现的? 2022-01-01
- 症状类型错误:无法确定关系的真值 2022-01-01