closures and dynamic binding

Aaron "Castironpi" Brady castironpi at gmail.com
Sun Sep 28 09:04:14 CEST 2008


On Sep 28, 1:14 am, Marc 'BlackJack' Rintsch <bj_... at gmx.net> wrote:
> On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi\" Brady wrote:
> > 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
>
> `n` is looked up at the time ``f[0]`` is called.  At that time it is
> bound to 9.
>
> >>>> 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?
>
> It *does* look up `n` at the time you call ``f[0]`` but this time it's
> the local `n` of the outer ``lambda`` function and that is bound to
> whatever the function was called with.  At the time it was called the
> global `n` was bound to 0.  Maybe it get's more clear if you don't name
> it `n`:
>
> In [167]: f = [None] * 10
>
> In [168]: for n in xrange(10):
>    .....:     f[n] = (lambda x: lambda: x)(n)
>    .....:
>
> In [169]: f[0]()
> Out[169]: 0
>
> Ciao,
>         Marc 'BlackJack' Rintsch

Hi Marc,

It's my understanding that 'n' gets a new value every time through the
loop.  n= 0 on the first pass, n= 1 on the second pass, and so on.  n=
9 by the end, and that's why `lambda: n` always returns 9.  It queries
the variable 'n', and finds 9.  (This got lengthy.  I started thinking
aloud.)

In your version of the indirect example, it queries the variable 'x',
which it finds in a new distinct scope in each f element.  In f[0], x=
0.  In f[1], x= 1.  There are 10 different 'x' variables throughout
the contents of f.  The direct example does not do this allocation of
ten different 'x's.

It's sort of helping.  I think I feel like the following is more like
what I'm looking for:

(Non-standard)
>>> f= [ None ]* 10
>>> for n in range( 10 ):
...     f[ n ]= n
...
>>> f[0]
 9
>>> f[1]
 9

because when you access f[0], it looks up the variable 'n'.  Obviously
not.

(Unproduced)
>>> f= [ None ]* 10
>>> for n in range( 10 ):
...     f[ n ]= late( n ) #late/lazy
...
>>> f[0]
 9
>>> f[1]
 9

>>> f= [ None ]* 10
>>> for n in range( 10 ):
...     f[ n ]= early( n ) #early/eager
...
>>> f[0]
 0
>>> f[1]
 1

For the functions, I want a function that returns 'n', either late or
early.

(Unproduced)
>>> for n in range( 10 ):
...     f[ n ]= lambda: late( n )
>>> f[0]()
9
>>> for n in range( 10 ):
...     f[ n ]= lambda: early( n )
>>> f[0]()
0

I don't think you could pull this off.  'late' and 'early' succeed
with quotes around n, early('n') and late('n'), in the direct
assignments, but the functions aren't so lucky.  'n' has gone on to a
better world by the time 'early' gets any control.

This might have some success.

(Unproduced)
>>> for n in range( 10 ):
...     f[ n ]= late( lambda: n )
>>> f[0]()
9
>>> for n in range( 10 ):
...     f[ n ]= early( lambda: n )
>>> f[0]()
0

Though it's beyond my foresight to tell if it's feasible as stated, if
you need quotes, how much introspection you would need, etc.  Plus,
'late' and 'early' were accepting quoted parameters earlier.  How
would they look inside a function?  Could a simple decorator provide
the service?

On a tangent about mutables, it's not that numbers, strings, and
tuples are 'immutable' per se, it's just that they don't have any
methods which mutate them (or assignable properties).  Lists and
dictionaries do.  It's up to the user whether a custom class does.



More information about the Python-list mailing list