override a property

Bengt Richter bokr at oz.net
Fri Oct 21 19:31:39 EDT 2005


On Tue, 18 Oct 2005 08:00:51 +0000, Robin Becker <robin at SPAMREMOVEjessikat.fsnet.co.uk> wrote:

>Bruno Desthuilliers wrote:
>> Robin Becker a écrit :
>> 
>>> Is there a way to override a data property in the instance? Do I need 
>>> to create another class with the property changed?
>> 
>> 
>> Do you mean attributes or properties ?
>
>I mean property here. My aim was to create an ObserverProperty class 
>that would allow adding and subtracting of set/get observers. My current 
>implementation works fine for properties on the class, but when I need 
>to specialize an instance I find it's quite hard.
>
Sorry, but my news feed went belly-up for a few days and I had to go to
google groups to see what had transpired.

ISTM you are already a fair way along the lines of Alex's suggestion of a custom
descriptor class, having chosen to create it by subclassing property.

If it is ok to add attributes to instances, you could put overriding
observer functions there and just check for them in your __notify_fset
loop, e.g., by checking for a matching name with an '__override_' prefixed
to the name of the obs function you want to override (see example below).

If it's not ok to add instance attributes, you could store the information
elsewhere, using weak-ref stuff as Alex suggests.

E.g. (I'm pasting in as a quote what I copied from google):


>bruno modulix wrote:
>.....
>> 
>> Could you elaborate ? Or at least give an exemple ?
>.....
>in answer to Bengt & Bruno here is what I'm sort of playing with. Alex suggests 
>class change as an answer, but that looks really clunky to me. I'm not sure what 
>Alex means by

>> A better design might be to use, instead of the builtin
>> type 'property', a different custom descriptor type that is specifically
>> designed for your purpose -- e.g., one with a method that instances can
>> call to add or remove themselves from the set of "instances overriding
>> this ``property''" and a weak-key dictionary (from the weakref module)
>> mapping such instances to get/set (or get/set/del, if you need to
>> specialize "attribute deletion" too) tuples of callables.

>I see it's clear how to modify the behaviour of the descriptor instance, but is 
>he saying I need to mess with the descriptor magic methods so they know what 
>applies to each instance?

I think maybe only insofar as __notify_fset is magic ;-)

>## my silly example
>class ObserverProperty(property):
>     def __init__(self,name,observers=None,validator=None):
>         self._name = name
>         self._observers = observers or []
>         self._validator = validator or (lambda x: x)
>         self._pName = '_' + name
>         property.__init__(self,
>                 fset=lambda inst, value: self.__notify_fset(inst,value),
>                 )

>     def __notify_fset(self,inst,value):
>         value = self._validator(value)
>         for obs in self._observers:
              obs = inst.__dict__.get('__override_'+obs.func_name, obs)

>             obs(inst,self._pName,value)
>         inst.__dict__[self._pName] = value

>     def add(self,obs):
>         self._observers.append(obs)

>def obs0(inst,pName,value):
>     print 'obs0', inst, pName, value

>def obs1(inst,pName,value):
>     print 'obs1', inst, pName, value

>class A(object):
>     x = ObserverProperty('x')

>a=A()
>A.x.add(obs0)

>a.x = 3

>b = A()
>b.x = 4

>#I wish I could get b to use obs1 instead of obs0
>#without doing the following
I think your class assignment would eliminate all x-observing functions,
but did you mean strictly just to "use obs1 instead of obs0" -- which
would mean to leave others operable?

## >class B(A):
## >     x = ObserverProperty('x',observers=[obs1])
##
## >b.__class__ = B

b.__override_obs0 = obs1

# Of course you could wrap that last line functionality in some helper thing ;-)

>b.x = 7

With the above mods put in becker.py, I get:

 >>> import becker
 obs0 <becker.A object at 0x02EF3C2C> _x 3
 obs0 <becker.A object at 0x02EF3C4C> _x 4
 obs1 <becker.A object at 0x02EF3C4C> _x 7

But adding another observer doesn't eliminate the other(s):

 >>> def obs3(inst,pName,value):
 ...     print 'obs3', inst, pName, value
 ...
 >>> becker.A.x.add(obs3)
 >>> becker.b.x = 777
 obs1 <becker.A object at 0x02EF3C4C> _x 777
 obs3 <becker.A object at 0x02EF3C4C> _x 777
 >>> becker.a.x = 777
 obs0 <becker.A object at 0x02EF3C2C> _x 777
 obs3 <becker.A object at 0x02EF3C2C> _x 777

HTH

Regards,
Bengt Richter



More information about the Python-list mailing list