python - What do (lambda) function closures capture? -
recently started playing around python , came around peculiar in way closures work. consider following code:
adders=[0,1,2,3] in [0,1,2,3]: adders[i]=lambda a: i+a print adders[1](3)
it builds simple array of functions take single input , return input added number. functions constructed in for
loop iterator i
runs 0
3
. each of these numbers lambda
function created captures i
, adds function's input. last line calls second lambda
function 3
parameter. surprise output 6
.
i expected 4
. reasoning was: in python object , every variable essential pointer it. when creating lambda
closures i
, expected store pointer integer object pointed i
. means when i
assigned new integer object shouldn't effect created closures. sadly, inspecting adders
array within debugger shows does. lambda
functions refer last value of i
, 3
, results in adders[1](3)
returning 6
.
which make me wonder following:
- what closures capture exactly?
- what elegant way convince
lambda
functions capture current value ofi
in way not affected wheni
changes value?
your second question has been answered, first:
what closure capture exactly?
scoping in python dynamic and lexical. closure remember name , scope of variable, not object it's pointing to. since functions in example created in same scope , use same variable name, refer same variable.
edit: regarding other question of how overcome this, there 2 ways come mind:
the concise, not strictly equivalent way one recommended adrien plisson. create lambda argument, , set argument's default value object want preserved.
a little more verbose less hacky create new scope each time create lambda:
>>> adders = [0,1,2,3] >>> 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 created using new function (a lambda, brevity), binds argument, , passing value want bind argument. in real code, though, have ordinary function instead of lambda create new scope:
def createadder(x): return lambda y: y + x adders = [createadder(i) in range(4)]
Comments
Post a Comment