Weird closure behavior in python

2019-01-06 22:24发布

问题:

I have a following simple code:

def get():
    return [lambda: i for i in [1, 2, 3]]

for f in get():
    print(f())

As expected from my python knowledge, output is 3 - entire list will contain last value of i. But how this works internally?

AFAIK, python variables are simply reference to objects, so first closure must enclose object first i reference - and this object is definitely 1, not 3 O_O. How it happens that python closure encloses variable itself instead of object this variable reference? Does it save variable name as plain text, some "reference to variable" or what?

回答1:

Closures don't refer to variables but rather to scopes. Since the last value of i in its scope is '3', all three closures return the same. To "lock" the current value of a variable, create a new scope just for it:

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )


回答2:

As @thg435 points out, a lambda will not encapsulate the values at that moment, but rather the scope. There are too small ways you can address this:

lambda default argument "hack"

[ lambda v=i: v for i in [ 1, 2, 3 ] ]

Or use functools.partial

from functools import partial
[ partial(lambda v: v, i) for i in [ 1, 2, 3 ] ]

Essentially you have to move the scope to be local to the function you are creating. Generally I like using partial more often since you can pass it a callable, and any args and kargs to create a callable with a proper closure. Internally, it is wrapping your original callable so the scope is shifted for you.



回答3:

Each lambda is actually referring to the same i, which is a variable created by the list comprehension. Upon termination of the list comprehension, i maintains the value of the final element that it was assigned to until it goes out of scope (which is prevented by encapsulating it within a function and returning it, namely the lambda). As others have pointed out, closures don't maintain copies of values, but rather maintain references to variables that were defined within their scope.