Observer implementation question ('Patterns in Python')

James Stroud jstroud at mbi.ucla.edu
Sat Oct 13 01:55:05 CEST 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