Class decorators do not inherit properly

Diez B. Roggisch deets at nospam.web.de
Fri Jul 13 05:02:44 EDT 2007


Chris Fonnesbeck schrieb:
> I have a class that does MCMC sampling (Python 2.5) that uses decorators 
> -- one in particular called _add_to_post that appends the output of the 
> decorated method to a class attribute. However, when I 
> subclass this base class, the decorator no longer works:
> 
> Traceback (most recent call last):
>   File "/Users/chris/Projects/CMR/closed.py", line 132, in <module>
>     class M0(MetropolisHastings):
>   File "/Users/chris/Projects/CMR/closed.py", line 173, in M0
>     @_add_to_post
> NameError: name '_add_to_post' is not defined
> 
> yet, when I look at the dict of the subclass (here called M0), I see the 
> decorator method:
> 
> In [5]: dir(M0)
> Out[5]: 
> ['__call__',
>  '__doc__',
>  '__init__',
>  '__module__',
>  '_add_to_post',
> ...
> 
> I dont see what the problem is here -- perhaps someone could shed 
> some light. I thought it might be the underscore preceding the name, 
> but I tried getting rid of it and that did not help.

Does this simple example show your problem?


class Meta(type):
     def __init__(cls, *args):
         print "Meta.__init__ called"
         return super(Meta, cls).__init__(*args)


class A(object):
     __metaclass__ = Meta

     def decorator(f):
         print "decorator called"
         return f

     @decorator
     def foo(self):
         pass


class B(A):
     #@decorator
     def bar(self):
         pass


print dir(A())
print dir(B())


then it explains the problem easily: the class-statement (class 
Name(base): <definitions) is evaluated _before_ the actual class is 
created. Thus at that moment, there is no decorator known in the 
surrounding scope.

Use a function level decorator instead, that delegates it's work to a 
classmethod/instancemethod. Something like this (can't be more precise 
as you didn't show us code):

def decorator(f):
     def _d(self, *args, **kwargs):
         self._a_decorator_method()
         return f(self, *args, **kwargs)
     return _d


Diez



More information about the Python-list mailing list