(lambda)函数闭包捕获了什么? [英] What do (lambda) function closures capture?

查看:111
本文介绍了(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=[0,1,2,3]

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

print adders[1](3)

它构建了一个简单的函数数组,这些函数接受单个输入并返回该输入加数字后的结果.这些函数在for循环中构造,其中迭代器i03运行.对于这些数字中的每一个,都会创建一个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的值不会受到影响的方式捕获i的当前值的最优雅的方法是什么?
  • What do the closures capture exactly?
  • What is the most elegant way to convince the lambda functions to capture the current value of i in a way that will not be affected when i changes its value?

推荐答案

您的第二个问题已经回答,但您的第一个问题是

Your second question has been answered, but as for your first:

闭包究竟捕获了什么?

what does the closure capture exactly?

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:

  1. 最简洁但并非严格等同的方法是 Adrien Plisson推荐的一种.创建带有附加参数的lambda,并将附加参数的默认值设置为要保留的对象.

  1. 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)函数闭包捕获了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆