Not a class decorator, but a decorator class.
I can write a decorator function and it works fine, but when I try the same implemented as a class (to wrestle out of the nesting, mainly), I get caught in the selfies. Here's a minimal working example:
Note The decorator is used with parentheses like so @DecoratorClass()
and not @DecoratorClass
. This makes a big difference.
def decorator_function():
def decorator(fcn):
def wrapper(self, *args):
print('decorator_function wrapper self:', self)
print('decorator_function wrapper args:', args)
return fcn(self, *args)
return wrapper
return decorator
class DecoratorClass:
def __init__(self):
self.fcn = None
def __call__(self, fcn):
self.fcn = fcn
return self.wrapper
def wrapper(self, *args):
print('DecoratorClass wrapper self:', self)
print('DecoratorClass wrapper args:', args)
return self.fcn(*args)
class A:
@decorator_function()
def x(self, *args):
print('A x self:', self)
print('A x args:', args)
return 42
@DecoratorClass()
def y(self, *args):
print('A y self:', self)
print('A y args:', args)
return 43
a = A()
a.x(1, 2)
a.y(1, 2)
And here is the output.
decorator_function wrapper self: <mytests.debugme.A object at 0x7fa540d315f8>
decorator_function wrapper args: (1, 2)
A x self: <mytests.debugme.A object at 0x7fa540d315f8>
A x args: (1, 2)
DecoratorClass wrapper self: <mytests.debugme.DecoratorClass object at 0x7fa540d31208>
DecoratorClass wrapper args: (1, 2)
A y self: 1
A y args: (2,)
As you can see, the class based approach loses the reference to a
. To clarify, I wish the result of a.y(1,2)
to be identical to a.x(1,2)
.
update for @Martijn Pieters
class DecoratorClass:
def __init__(self):
self.fcn = None
def __call__(self, fcn):
self.fcn = fcn
return self.wrapper
def __get__(self, obj, objtype):
"""Support instance methods."""
import functools
return functools.partial(self.__call__, obj)
def wrapper(self, *args):
print('DecoratorClass wrapper self:', self)
print('DecoratorClass wrapper args:', args)
return self.fcn(*args)
a = A()
a.y(1, 2)
Output:
DecoratorClass wrapper self: <mytests.debugme.DecoratorClass object at 0x7fa540d31048>
DecoratorClass wrapper args: (1, 2)
A y self: 1
A y args: (2,)
IOW: the marked duplicate question has no working answer.