Observer implementation question ('Patterns in Python')
James Stroud
jstroud at mbi.ucla.edu
Fri Oct 12 19:55:05 EDT 2007
Anders Dahnielson wrote:
> Sorry if this is a somewhat silly question...
>
> I'm using the Observer implementation found in 'Patterns in
> Python' [1] and find it really neat. But, I have not yet fully groked
> the new-style class, classic class and property differences involved.
> So can somebody please explain to me why this works (using a classic
> class):
>
> class C:
> TheEvent = Event()
> def OnTheEvent(self):
> self.TheEvent(self, context)
>
> instance = C()
> instance.TheEvent += callback
> instance.OnTheEvent()
> instance.TheEvent -= callback
>
> While this do not (using new-style class):
>
> class C(object):
> TheEvent = Event()
> def OnTheEvent(self):
> self.TheEvent(self, context)
>
> instance = C()
> instance.TheEvent += callback
> instance.OnTheEvent()
> instance.TheEvent -= callback
>
> Raising an AttributeError :
>
> Traceback (most recent call last):
> File "sig_test.py", line 7, in ?
> instance.TheEvent += callback
> AttributeError: can't set attribute
>
> So far I've figured out that it is the Event class (subclassing the
> built-in property type) that is the culprit, but not why. (See
> 'Patterns in Python' [1] for code listing of the Event class.)
>
> I would be grateful if some helpful soul could explain it.
>
> [1] http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html#observer
>
This is because the object derived class C is behaving properly with
respect to its property attributes. __iadd__() is supposed to set the
attribute, but no setter is called because the property (an Event())
does not have a setter defined. The most straightforward fix would not
be to define a setter in construction of Event instances, but would
rather to come up with some other way than __iadd__ to specify changing
the state of the delegates:
# was __iadd__()
def append(self, callback):
self.__delegates = [ cb
for cb in self.__delegates
if getattr(cb, 'im_self', None) != callback]
# If callback is callable, remove the last
# matching callback
if callable(callback):
for i in range(len(self.__delegates)-1, -1, -1):
if self.__delegates[i] == callback:
del self.__delegates[i]
break
[...]
d.append(function)
The use of __iadd__ & __isub__ as described in the article allows a neat
shorthand, but does not function correctly in the context of new style
classes.
James
--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095
http://www.jamesstroud.com
More information about the Python-list
mailing list