Dynamic use of property() fails
Bruno Desthuilliers
bruno.42.desthuilliers at websiteburo.invalid
Tue Apr 15 04:06:21 EDT 2008
andrew cooke a écrit :
> Hi,
>
> This is my first attempt at new classes and dynamic python, so I am
> probably doing something very stupid... After reading the how-to for
> descriptors at http://users.rcn.com/python/download/Descriptor.htm I
> decided I would make an object that returns attributes on read, but on
> setting calls an arbitrary function.
>
> My code looks like:
> class ActiveDAO(object):
> def __init__(self):
> self.__values__ = {}
__names__ are reserved for the Python implementation itself. Use _names
for 'protected' attributes.
> def add_field(self, name, value, on_change):
> self.__values__[name] = value
> def get(self): return self.__values__[name]
> def set(self, new_value): self.__values__[name] =
> on_change(new_value)
> def delete(self): raise AttributeError
> self.__dict__[name] = property(get, set, delete)
>
> However, when I try to use this (in a test) with code like:
> dao = ActiveDAO()
> dao.add_field("name", "value", lambda _: None)
> assertEqual(dao.name, "value")
>
> I get a failure because lookup of the attribute is returning
> "<property object at 0x6b8910>".
>
> That is quite reasonable, but I was under the expression that some
> magic was supposed to happen, as described in the document referenced
> above!
>
> Please can someone explain why there is no magic? :o(
Others already answered this.
The canonical solution is to use a custom descriptor instead of a property:
class Field(object):
def __init__(self, name, onchange):
self.name = name
self.onchange = onchange
def __get__(self, instance, cls):
if instance is None:
# called on the class
return self
# called on instance
return instance._values[self.name]
def __set__(self, instance, value):
instance._values[name] = self.onchange(value)
class ActiveDAO(object):
def __init__(self):
self._values = []
class Person(ActiveDAO):
name = Field('firstname', lambda v: v.strip().capitalize())
age = Field('age', lambda v : int(v))
Now you may want to search here or in the cookbook to learn how to:
- dynamically create new classes
- avoid having to repeat the name of the field (usually done using a
metaclass and a two-stages initialisation of Field objects)
HTH
More information about the Python-list
mailing list