Dynamic methods and lambda functions
mdw at distorted.org.uk
Sat Jan 24 01:29:08 CET 2009
unineuro at gmail.com writes:
> class Person:
> def __init__(self):
> for prop in props:
> setattr(self, "__" + prop, prop)
> setattr(Person, "Get" + prop, lambda self: getattr
> (self, "__" + prop))
> 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
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?
More information about the Python-list