Robin Becker <[EMAIL PROTECTED]> wrote: ... > 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
Changing class is indeed 'clunky', though it might have been necessary depending on how one interpreted your original specs. > 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? If (e.g.) __set__ needs to behave differently when applied to certain instances rather than others, then it had better be "messed with" (overridden) compared to property.__set__ since the latter has no such proviso. Of course, your architecture as sketched below (taking advantage of the fact that property.__set__ always calls a certain callable, and you get to control that callable) is OK too. > ## 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), > ) Why not just fset=self.__notify_fset ? I fail to see the added value of this lambda. Anyway...: > def __notify_fset(self,inst,value): > value = self._validator(value) > for obs in self._observers: > obs(inst,self._pName,value) > inst.__dict__[self._pName] = value > > def add(self,obs): > self._observers.append(obs) ...this class only offers sets of observers *per-descriptor instance*, not ones connected to a specific 'inst' being observed. My point is, you could add the latter pretty easily. > 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 > class B(A): > x = ObserverProperty('x',observers=[obs1]) > > b.__class__ = B > > b.x = 7 You can, if you have a way to call, say, b.x.add_per_inst(b, obs1). Such as, adding within ObserverProperty: self._observers_per_inst = {} in the init, and changing the notification method to do: def __notify_fset(self,inst,value): value = self._validator(value) observers = self._observers_per_inst.get(inst) if not observers: observers = self._observers for obs in observers: obs(inst,self._pName,value) inst.__dict__[self._pName] = value and a new method add_per_inst: def add_per_inst(self, inst, obs): self._observers_per_inst.setdefault(inst,[]).append(obs) Of course, you most likely want to use weak rather than normal references here (probably to both instances and observers), but that's a separate issue. Alex -- http://mail.python.org/mailman/listinfo/python-list