Just beware that both the decorator and the decorated function are unbound methods, so you can only access the self (or cls for classmethods) in the inner scope of the decorator, and must manually bind the decorated method to the instance bound in the inner decorator.
class A: x = 5 y = 6 def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator @decorate def func(self): return self.y A().func() # 30!!
Still trying to wrap my head around how decorators could be inherited and overridden.
Beware that for the decorator to work it can't be bound to an instance. That is: there is no way to make this work
a = A() @a.decorate def func(*args): return 1
Despite this pattern is much more common than the asked here.
At this point the question raises: is it a method at all or just code that you happen to hide in a class?
The only way to prevent the decorator being wrongfully bound is to declare it as a staticmethod, but then it must be in a previous super class because to be used it must be bound to the static class reference which would not be yet defined, just as the self.
class A: x = 1 @staticmethod def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator class B(A): @A.decorate def func(self): return 1 class C(): x = 2 @B.decorate def func(self): return 1 a = A() class D(): x = 3 @a.decorate def func(self): return 1 B().func() # 1 C().func() # 2 D().func() # 3
But as you can see, there is no way for the decorator to use the state of its own class. class A from this last example just happens to be a mixin with a default x variable and an "unrelated" static decorator.
So, again, is it a method?
To overcome all of this, you can bind the staticmethod in your same class to an arbitrary type. Namely, the builtin type will do.
class A: x = 1 @staticmethod def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator @decorate.__get__(type) def func(self): return 1 class B: x = 2 @A.decorate def func(self): return 1 class C: x = 3 @(A().decorate) # Only for Python 3.9+, see PEP-614 def func(self): return 1 A().func() # 1 B().func() # 2 C().func() # 3
But this features too much magic for my taste. And still not a method for my gut.
actionis supposed to do.Baseclass, and import it together withBase. Give it a leading underscore name so thatfrom module import *will not include it and any Python developer worth its salt will recognize it as a private utility function.