Let’s suppose we need a decorator that we gonna use inside one specific class only. Something like this:
Where to define this decorator?
Function inside class
The immediate urge is to put
atomic_rating_change into the class itself:
That would actually work, but there is major flaw in this approach:
atomic_rating_change becomes an instance method of the
User class. That doesn’t make any sense. More than this, it doesn’t even work as a method: if you call it, the
decorated parameter will be used as
Note, that within the class body
atomic_rating_change is still a plain function. It becomes a method only after the class is actually created, and at this time the decorator is already successfully applied.
Here is a small example that may help you understand this concept:
You can see indeed that
func is a function within the class body, but it’s a method outside of the class. Moreover,
A().func values differ too, thanks to Python descriptor magic (if you are unfamiliar with descriptors in Python, you should check the link). In Python 3
A.func is just a plain function though.
What can we do to make this work properly? Here are some ideas.
Make it static (don’t)
The simple thing you do every time you want to store just a plain function in a class is just to make it a static method.
You may think that it resolves all the problems completely, but sadly it doesn’t.
staticmethod is another descriptor, and descriptors behave differently inside and outside of a class (yeah, again).
So, once you apply this decorator to some method you get
TypeError: 'staticmethod' object is not callable, because you see a raw descriptor inside the class.
The most simple solution you are probably already thinking of is to declare function outside of the class.
The only downside is the function is not anyhow bound to the class anymore.
Declare in another class
To fix this we can declare a helper class that contains all the decorators for
UserDecorators isn’t bound to the original class either (save the name), but since it’s not a function anymore, we can put it in
Declare in inner class
You also can make
Decorators private (by renaming it to
_Decorators) if you don’t like other classes to use your decorator.
This solution is pretty solid and recommended, but there is one appealing method I’m aware of.
Make class, not method
Thanks to Python
__call__ magic method, you can declare any decorator as a class, not as a function. Recall, that our initial problem is functions behaving differently inside and outside of a class body, but that doesn’t apply at all to classes inside a class body (look at the
Decorators in the previous examples: it works just fine). So converting the decorator from the first example to a class would work perfectly.
Fun fact: both working solutions involve changing the inner function to the inner class (due to the specific behaviour of function attributes, duh).
Note that the described problem is not of a conceptual nature, it’s just a way to fight the fact that a function can’t be a simple class attribute in Python.