开发者

Have Python 2.7 functions remember value and not reference? Closure Weirdness

开发者 https://www.devze.com 2023-03-21 17:14 出处:网络
I\'m trying to return from a function a list of functions, each of which uses var开发者_开发技巧iables from the outside scope. This isn\'t working. Here\'s an example which demonstrates what\'s happen

I'm trying to return from a function a list of functions, each of which uses var开发者_开发技巧iables from the outside scope. This isn't working. Here's an example which demonstrates what's happening:

a = []
for i in range(10):
    a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2

Why is this happening, and how can I get around it in python 2.7 ?


The i refers to the same variable each time, so i is 9 in all of the lambdas because that's the value of i at the end of the loop. Simplest workaround involves a default argument:

lambda x, i=i: x+i

This binds the value of the loop's i to a local variable i at the lambda's definition time.

Another workaround is to define a lambda that defines another lambda, and call the first lambda:

(lambda i: lambda x: x+i)(i)

This behavior makes a little more sense if you consider this:

def outerfunc():

    def innerfunc():
        return x+i

    a = []
    for i in range(10):
        a.append(innerfunc)
    return a

Here, innerfunc is defined once, so it makes intuitive sense that you are only working with a single function object, and you would not expect the loop to create ten different closures. With a lambda it doesn't look like the function is defined only once, it looks like you're defining it fresh each time through the loop, but in fact it is functionally the same as the the long version.


Because i isn't getting evaluated when you define the anonymous function (lambda expression) but when it's called. You can see this by adding del i before a[1](1): you'll get NameError: global name 'i' is not defined on the a[1](1) line.

You need to fix the value of i into the lambda expression every time, like so:

a = [lambda x, i=i: x+i for i in range(10)]
a[1](1) # returns 2


Another, more general solution - also without lambdas:

import operator
from functools import partial
a = []
for i in range(10):
    a.append(partial(operator.add, i))
a[1][(1) # returns 2

The key aspect here is functools.partial.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号