I've implemented decorator that can receive extra arguments and want to use it with class methods. I want to pass @property as decorator argument, but instead of @property result I got this:
<property object at 0x7f50f5195230>
This is my decorator:
class Decorator(object):
def __init__(self, some_arg):
self.func = None
self.some_arg = some_arg
def __get__(self, instance, owner):
import functools
return functools.partial(self.__call__, instance)
def __call__(self, func):
self.func = func
def wrapper(*args, **kwargs):
return self._process_sync(*args, **kwargs)
return wrapper
def _process_sync(self, *args, **kwargs):
try:
print(self.some_arg)
return self.func(*args, **kwargs)
except Exception as e:
print(e)
return None
My test class:
class Test(object):
@property
def some_data(self):
return {'key': 'value'}
@Decorator(some_data)
def some_method(self):
print('method output')
return None
Usage:
test = Test()
test.some_method()
Two questions:
<property object at 0x7f50f5195230>
A property
object is a descriptor. To get a value out of it, you need to call its __get__
method with an appropriate instance. Figuring out when to do that in your current code is not easy, since your Decorator
object has a bunch of different roles. It's both a decorator factory (getting initialized with an argument in the @Decorator(x)
line), and the decorator itself (getting called with the function to be decorated). You've given it a __get__
method, but I don't expect that to ever get used, since the instance of Decorator
never gets assigned to a class variable (only the wrapper function that gets returned from __call__
).
Anyway, here's a modified version where the Decorator
handles almost all parts of the descriptor protocol itself:
class Decorator:
def __init__(self, arg):
self.arg = arg # this might be a descriptor, like a property or unbound method
def __call__(self, func):
self.func = func
return self # we still want to be the descriptor in the class
def __get__(self, instance, owner):
try:
arg = self.arg.__get__(instance, owner) # try to bind the arg to the instance
except AttributeError: # if it doesn't work, self.arg is not a descriptor, that's OK
arg = self.arg
def wrapper(*args, **kwargs): # this is our version of a bound method object
print(arg) # do something with the bound arg here
return self.func.__get__(instance, owner)(*args, **kwargs)
return wrapper