closures and dynamic binding

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Sep 28 03:52:56 EDT 2008


On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi\" Brady wrote:

> Hello all,
> 
> To me, this is a somewhat unintuitive behavior.  I want to discuss the
> parts of it I don't understand.
> 
>>>> f= [ None ]* 10
>>>> for n in range( 10 ):
> ...     f[ n ]= lambda: n
> ...
>>>> f[0]()
> 9
>>>> f[1]()
> 9
> 
> I guess I can accept this part so far, though it took a little getting
> used to.  I'm writing some code and found the following workaround, but
> I don't think it should give different results.  Maybe I'm not
> understanding some of the details of closures.
> 
>>>> f= [ None ]* 10
>>>> for n in range( 10 ):
> ...     f[ n ]= (lambda n: ( lambda: n ) )( n )
> ...
>>>> f[0]()
> 0
>>>> f[1]()
> 1
> 
> Which is of course the desired effect.  Why doesn't the second one just
> look up what 'n' is when I call f[0], and return 9?

That's an awfully complicated solution. A much easier way to get the 
result you are after is to give each function its own local copy of n:

f[n] = lambda n=n: n

As for why the complicated version works, it may be clearer if you expand 
it from a one-liner:

# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )

inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)

outer(0) => inner with a local scope of n=0
outer(1) => inner with a local scope of n=1 etc.

Then, later, when you call inner() it grabs the local scope and returns 
the number you expected.


-- 
Steven



More information about the Python-list mailing list