super, decorators and gettattribute

Mike Meyer mwm-keyword-python.b4bdba at mired.org
Sat Jan 12 15:47:05 EST 2008


On Sat, 12 Jan 2008 10:45:25 -0800 (PST) Richard Szopa <ryszard.szopa at gmail.com> wrote:

> Hello all,
> 
> I am playing around w/ Python's object system and decorators and I
> decided to write (as an exercise) a decorator that (if applied to a
> method) would call the superclass' method of the same name before
> doing anything (initially I wanted to do something like CLOS
> [1] :before and :end methods, but that turned out to be too
> difficult).
> 
> However, I cannot get it right (specially, get rid of the eval). I
> suspect that I may be misunderstanding something happening between
> super objects and __getattribute__ methods.
> 
> Here's my code:
> 
> def endmethod(fun):
>     """Decorator to call a superclass' fun first.
> 
>     If the classes child and parent are defined as below, it should
>     work like:
> 
>     >>> x = child()
>     >>> x.foo()
>     I am parent's foo
>     I am child's foo.
>     """
>     name = fun.__name__
>     def decorated(self, *args, **kwargs):
>         try:
>             super_object = super(self.__class__, self)

There's an apparently common bug here: you don't want to pass super
self.__class__, but the class that the method is bound to. The two
aren't the same, as an instance of a subclass will have the subclass
as self.__class__, and not the current class. So super will return the
current class or a subclass of it, meaning (since you invoked this
method from self) you'll wind up invoking this method recursively.
All of which means your decorator is probably going to have to take
the class as an argument.

>             # now I want to achieve something equivalent to calling
>             # parent.foo(*args, **kwargs)
>             # if I wanted to limit it only to this example
> 
>             # this doesn't work: in the example, it calls child's foo,
>             # entering in an eternal loop (instead of calling parent's
>             # foo, as I would expect).
> 
>             # super_object.__getattribute__(name)(*args, **kwargs)
> 
>             # this does work, but I feel it's ugly
>             eval('super_object.%s(*args, **kwargs)' % name)
>         except AttributeError:
>             pass # if parent doesn't implement fun, we don't care
>                  # about it
>         return fun(self, *args, **kwargs) # hopefully none
> 
>     decorated.__name__ = name
>     return decorated
> 
> 
> class parent(object):
>     def foo(self):
>         print 'I am parent\'s foo'
> 
> class child(parent):
>     @endmethod
>     def foo(self):
>         print "I am foo\'s foo."
> 
> if __name__=='__main__':
>     x = child()
>     x.foo()
> 
> Can anybody tell me how to call a superclass method knowing its name?

The same way you call any object's methods if you know it's name":

    getattr(super_object, name)(*args, **kwargs)

The code seems to work the way you want:

>>> x.foo()
I am parent's foo
I am foo's foo.

     <mike
-- 
Mike Meyer <mwm at mired.org>		http://www.mired.org/consulting.html
Independent Network/Unix/Perforce consultant, email for more information.



More information about the Python-list mailing list