Lambda forms and scoping
R. David Murray
rdmurray at bitdance.com
Fri Mar 20 08:28:08 EDT 2009
Benjamin Peterson <benjamin at python.org> wrote:
> Márcio Faustino <m.faustino <at> gmail.com> writes:
> >
> > Executing the example below doesn't produce the expected behavior, but
> > using the commented code does. Is this normal, or is it a problem with
> > Python? I've tested it with version 2.6.1 on Windows XP.
> >
> > Thanks,
> >
> > --
> >
> > from abc import *
> > from types import *
> > import re
> >
> > class Base (ObjectType):
> > __metaclass__ = ABCMeta
> >
> > def __init__(self):
> > for option in self.get_options().keys():
> > method = 'get_%s_option' % re.sub(' ', '_', option.lower
> > ())
> > setattr(self.__class__, method, lambda self:
> > self.get_option(option))
>
> This is because the closure over option is changed when it is reassigned in the
> for loop. For example:
>
> >>> def f():
> ... return [lambda: num for num in xrange(2)]
> ...
> >>> f()
> [<function <lambda> at 0x83f30>, <function <lambda> at 0x83e70>]
> >>> f()[0]
> <function <lambda> at 0x83ef0>
> >>> g = f()
> >>> g[0]()
> 1
> >>> g[1]()
> 1
Here's the way I find it useful to think about this:
When your lambda is created in your for loop inside your __init__ method,
it acquires a pointer to __init__'s local namespace. (That's how I
understand what "closure" means in this case, though I imagine "closure"
probably means something slightly different in computer-science-ese :)
So, when any of those lambdas is executed, they all have a pointer to
the exact same namespace, that of __init__. And when they are called,
__init__'s namespace is in whatever state it was left in when __init__
ended. In this case, that means that 'option' is pointing to the value
it had at the _end_ of the for loop.
Hope this helps. I find that thinking in terms of namespaces helps
me understand how Python works better than any other mental model
I've come across.
--
R. David Murray http://www.bitdance.com
More information about the Python-list
mailing list