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