Python, Как добавить еще один декоратор для фильтрации вывода существующих мульти декораторов со свойством в python? - PullRequest
1 голос
/ 02 мая 2019

У меня есть 2 существующих декоратора для работы в python, @ property и @ safe_property .Эти декораторы не могут быть изменены, и они являются частью кода, к которому у меня нет доступа.


def safe_property(original_property):
    def wrap(self):
        try:
            return original_property(self)
        except AttributeError as e:
            pass
    return wrap

class MyClass(object):
    def __init__(self):
        pass

    @property
    @safe_property
    def do_func(self):
        print("inside do_func!")
        return [2,3,4]

Вызвав функцию:

a = MyClass()
print(a.do_func)

Вывод мне подходит!:


inside do_func!
[2, 3, 4]

Теперь пришла другая функция, и япопытка отфильтровать некоторые возвращаемые значения do_func в соответствии с (необязательно) дополнительным аргументом.Это означает, что некоторые пользователи могут продолжать работу как обычно и звонить:

print(a.do_func)

В то время как другие могут звонить с фильтром:

print(a.do_func(True))

Чтобы попробовать это, я создал другой декоратор с именем my_decorator , например:

def my_decorator(*args, **kwargs):

    print(args)
    print(kwargs)
    def wrapper(*args):
        print(args)
        if args[1] == True:
            return
            # how do I return filter?
        else:
            return #without the filter?
    return wrapper


class MyClass(object):
    def __init__(self):
        pass

    @my_decorator
    @property
    @safe_property
    def do_func(self):
        print("inside do_func!")
        return [2,3,4]

Текущий вывод этой функции:

(<property object at 0x02AF0090>,)
{}
(<__main__.MyClass object at 0x00BCDBB0>, True)
None

Как я могу отфильтровать только нечетное число **, например, ** из списка возврата: do_func ?

Спасибо

1 Ответ

2 голосов
/ 02 мая 2019

Вы применяете свой декоратор к выходу декоратора @property.Этот декоратор создает объект property() , а не функцию.Это потому, что декораторы применяются наружу из определения функции;см. мой ответ о порядке выполнения декоратора ;поэтому сначала применяется @safe_property, затем @property, затем @my_decorator.

Если вы хотите украсить функцию получения, поместите ваш декоратор прямо над оператором def, он будет выполнен первыми все, что возвращает ваш декоратор, будет передано декоратору safe_property() (который добавляет свою собственную функцию-обертку):

@property
@safe_property
@my_decorator
def do_func(self):
    print("inside do_func!")
    return [2,3,4]

или, если смотреть @safe_property, также создает функцию-обертку, которая подходит в качестве получателяФункция, вы можете поместить ваш декоратор между строками @safe_property и @property, чтобы обернуть обертку, возвращенную предыдущую функцию:

@property
@my_decorator
@safe_property
def do_func(self):
    print("inside do_func!")
    return [2,3,4]

В любом случае, обертка вашего декоратора передается вызываемой для украшения, идолжен вернуть замену.Получатели свойств принимают только self, ваша замена будет вызвана также self, и никаких других аргументов:

def my_decorator(func):
    def wrapper(self):  # a replacement getter function, so only self is passed in
        result = func(self)  # call the original getter
        if self.some_flag:  # you can access the instance in the wrapper
            # return only odd values from the getter
            return [i for i in result if i % 2]
        else:
            # otherwise return the values unchanged
            return result
    return wrapper

Поместить @my_decorator вверху означает украсить property() объект,это не функция, поэтому вам нужно специально обрабатывать передачу такого объекта (вы можете увидеть, как @property работает в ответе, который я написал до ).

Например, выможет извлечь получатель из атрибута property().fget, а затем вернуть соответствующую замену (которая будет другим property() объектом):

def my_decorator(prop):
    getter = prop.fget
    def wrapper(self):  # a replacement getter function, so only self is passed in
        result = getter(self)  # call the original getter, taken from the property
        if self.some_flag:  # you can access the instance in the wrapper
            # return only odd values from the getter
            return [i for i in result if i % 2]
        else:
            # otherwise return the values unchanged
            return result
    # return a new property object, with the wrapper as the getter function
    # and copying across all other property attributes
    return property(wrapper, prop.fset, prop.fdel, prop.doc)

Обратите внимание, что свойство getterфункция будет только когда-либо передана self, другие аргументы для методов получения свойств невозможны.

Однако обработка объекта property напрямую не имеет никаких преимуществ перед размещением декоратора на одну строку ниже,это только усложняет ситуацию, добавляя ссылку prop.fget и property(...) возвращаемое значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...