On Jun 2, 3:00 pm, Peter Otten <[EMAIL PROTECTED]> wrote: <snip> > Then you have modified the code posted by Steven Bethard. > > > I don't see how your behaviour should come about ... a new observer-list > > is created for every decorated method, so there is no problem. > > Yes, but that list is shared across instances of SomeActor. > > > Now I'm confused ?-| > > You start with one Observable per method: > > >>> SomeActor.meth > > <tmp.Observable object at 0x401d5b0c>>>> SomeActor.meth is SomeActor.meth > > True > > This Observable knows nothing about a SomeActor instance (because there is > none). > > >>> SomeActor.meth.instance is None > > True > > Everytime you access meth from a SomeActor instance the __get__() method is > invoked and a new Observable is created, this time with an instance: > > >>> SomeActor().meth is SomeActor.meth > False > >>> SomeActor().meth.instance > > <tmp.SomeActor object at 0x401d56ec> > > But all of these Observables share the same observables list > > >>> SomeActor().meth.observers is SomeActor.meth.observers > True > >>> SomeActor().meth.observers is SomeActor().meth.observers > > True > > If you want per-instance callbacks you have to store the observers list (or > the bound method) in the instance: > > >>> class SomeActor(object): > > ... def __init__(self): > ... self.meth = Observable(self.meth, self) > ... def meth(self, foo): print foo > ...>>> a = SomeActor() > >>> b = SomeActor() > >>> def make_callback(s): > > ... def f(instance): > ... print s, instance > ... return f > ...>>> a.meth.add_callback(make_callback("alpha")) > >>> b.meth.add_callback(make_callback("beta")) > >>> a.meth(1) > > 1 > alpha <__main__.SomeActor object at 0x401d5c6c>>>> b.meth(2) > > 2 > beta <__main__.SomeActor object at 0x401d5ccc> > > Note that with this approach Observable need not be a descriptor; I was just > too lazy to rewrite it.
Here's my attempt at an implementation that works as a decorator. It stores the observers in a dictionary attribute of the instance, using the method name as a key. For example: >>> a = SomeActor() >>> a.meth.add_callback(callback) >>> a._observers {'meth': [<function callback at 0x00C29870>]} >>> a.meth.observers is a._observers['meth'] True ###################################################################### class Observable(object): def __init__(self, func, instance=None): self.func = func self.instance = instance if instance is not None and not hasattr(self.instance, '_observers'): self.instance._observers = {} def __get__(self, obj, cls=None): if obj is None: return self else: func = self.func.__get__(obj, cls) return Observable(func, obj) def __call__(self, *args, **kwargs): result = self.func(*args, **kwargs) for observer in self.observers: observer(self.instance) return result @property def observers(self): func_name = self.func.im_func.func_name return self.instance._observers.setdefault(func_name,[]) def add_callback(self, callback): self.observers.append(callback) ###################################################################### This implementation has a few drawbacks that I can see: * It doesn't allow for the syntax SomeActor.meth(instance, *args). I haven't spent the time to make it work on unbound as well as bound methods. * It may not play well with inheritance. If the subclass overrides an Observable method of the superclass, and declares its override Observable as well, the callback will be called twice. I'm not sure exactly how to handle this. * Perhaps it would be better to use the function object itself as the dictionary key, rather than its name? Anyway, these are just my initial thoughts -- I don't have the time to really think this through thoroughly. -- David -- http://mail.python.org/mailman/listinfo/python-list