Python3 невозможно передать @property в качестве аргумента декоратора - PullRequest
1 голос
/ 27 октября 2019

Я реализовал декоратор, который может получать дополнительные аргументы и хочет использовать его с методами класса. Я хочу передать @property в качестве аргумента декоратора, но вместо результата @property я получил это:

<property object at 0x7f50f5195230>

Это мой декоратор:

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

Мой тестовый класс:

class Test(object):
    @property
    def some_data(self):
        return {'key': 'value'}

    @Decorator(some_data)
    def some_method(self):
        print('method output')
        return None

Использование:

test = Test()
test.some_method()

Два вопроса:

  1. Как правильно передать свойство для получения результата @property вместо <property object at 0x7f50f5195230>
  2. Можно ли передать свойства / методы класса декоратору, если они указаны ниже в коде?

1 Ответ

2 голосов
/ 27 октября 2019

A property объект является дескриптором. Чтобы получить из него значение, вам нужно вызвать его метод __get__ с соответствующим экземпляром. Выяснить, когда это сделать в вашем текущем коде, непросто, поскольку ваш Decorator объект выполняет множество разных ролей. Это и фабрика декораторов (инициализируемая аргументом в строке @Decorator(x)), и сам декоратор (вызываемый с помощью функции, подлежащей декорированию). Вы дали ему метод __get__, но я не ожидаю, что он когда-либо будет использоваться, поскольку экземпляр Decorator никогда не будет назначен переменной класса (только функция-обертка, которая возвращается из __call__).

В любом случае, вот модифицированная версия, где Decorator обрабатывает почти все части самого протокола дескриптора:

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
...