Observer-Pattern by (simple) decorator

Peter Otten __peter__ at web.de
Sat Jun 2 10:00:48 EDT 2007


Wildemar Wildenburger wrote:

> David Wahler wrote:
>> Is this desired behavior?
>>
>>   
>>>>> a = SomeActor()
>>>>> b = SomeActor()
>>>>> a.meth.observers is b.meth.observers
>>>>>         
>> True
>>   
> No it's not, but I get
> 
>>>> a = SomeActor()
>>>> b = SomeActor()
>>>> a.meth.observers is b.meth.observers
> False

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.

Peter



More information about the Python-list mailing list