Dynamic methods and lambda functions

Mark Wooding mdw at distorted.org.uk
Fri Jan 23 19:29:08 EST 2009


unineuro at gmail.com writes:

> class Person:
>     def __init__(self):
>         for prop in props:
>             setattr(self, "__" + prop[0], prop[1])
>             setattr(Person, "Get" + prop[0], lambda self: getattr
> (self, "__" + prop[0]))

[...]

> The attributes are right, but the getter are not working. The problem
> is that the lambda function always execute the last parameter passed
> for all instances of the methods. How could it be done the right way?

Ahh!  Binding-versus-assignment confusion!

Things to realise:

  * There are two mappings of interest when dealing with variables.  The
    `binding' of a name maps the name to a variable.  The variable
    stores a (reference to a) value.

  * Assignment stores a new (reference to a) value in the variable.

  * Binding modifies the mapping between names and variables.

  * Nested functions, as constructed by `lambda' or `def', inherit (we
    say `close over') the bindings visible in their surroundings.

  * Python's `for' loop works by assignment.

So, what happens is this.  In `__init__', there's a binding of the name
`prop' to a variable.  The `for' loop modifies this variable on each
iteration, making it refer to successive elements of the `props' list.
The `lambda's you create inherit the binding of `prop' from the
surrounding `__init__', so the `lambda' sees the same variable as is
being modified by the loop.  Result: when you actually come to invoke
one of the `lambda's -- after `__init__' finishes -- you find that its
notion of `prop' is whatever the `for' left it as.

To fix the problem, you need to make a separate variable for each
iteration through the loop, and have the `lambda' close over that.
Function parameters are the easy approach to making fresh variables with
given contents: so we just call a function which accepts, and stores in
a parameter, the value we want the `lambda' to see, and closes over the
parameter.

        class Person (object):                  # use new-style classes
          def __init__(self):                   # allow parametrization
            def getter(propname):               # close over new variable
              return lambda self: getattr(self, propname)
            for name, value in props:           # destructuring wins
              setattr(self, '__' + name, value)
        for name, value in props:               # only need to do this once
          setattr(Person, 'Get' + name, getter('__' + name))

Does that make sense?

-- [mdw]



More information about the Python-list mailing list