Python decorator class to decorate standalone functions and object methods
The advanced technique that I describe below assumes you understand basic mechanic of Python decorators and Python descriptors.
Suppose we want to decorate method func_to_wrap
of the class ClassToWrap
.
If we create Python decorator class as below:
We will fail to use it:
func_to_wrap() missing 1 required positional argument: 'self'
As we can see from the error Decorator.__call__
calls func_to_wrap
without
self
. This is because orig_func
is unbound
,
it’s not bound to object instance and we have to pass the instance as first argument.
But we cannot!
The self
inside Decorator.__call__
is the instance of Decorator
.
We do not have ClassToWrap
instance inside Decorator.__call__
.
Of cause there is simple solution - write the decorator as function.
But I am goin to show you how to write decorator as class, using Python descriptors.
Moreover the decorator will be universal and could be applied to standalone functions as well as to object methods.
The Python descriptor protocol is very simple - if object attribute has method __get__
then Python will call it and return as the attribute value the result of __get__
.
Below you can see working example.
The UniversalDecorator.__call__
will work if we decorate standalone functions.
For class method instead of __call__
Python will call __get__
and after that call the result of it as a function.
UniversalDecorator.__get__
returns WrapperHelper
with links to both ClassToWrap
and Decorator
instances.
When Python call the __get__
result (instance of WrapperHelper
) it will actually call
WrapperHelper.__call__
.
WrapperHelper.__call__
add instance of ClassToWrap
, as first item in *args
(line
6). So func_to_wrap
will get instance of ClassToWrap
as first argument.