python 闭包 lambda,(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)

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 number a lambda funciton 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

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 adders1 returning 6.

Which me the following:

what does the closures capture exactly?

What is the most elegant way to convince that lambda functions to capture the current value of i and in a way that will not be affected when i changes it's value.

解决方案

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

what does the closure capture exactly?

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.

EDIT: Regarding your other question of how to overcome this, there are two ways that come to mind:

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.

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

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)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值