Декоратор свойств с необязательным аргументом - PullRequest
0 голосов
/ 17 октября 2019

Я хочу создать свойство класса с декоратором, который принимает необязательный аргумент. Обычно я писал бы

def MyProperty(func, optional=None):
    def getter():
        """magic goes here"""
        return func() if not optional else optional(func())
    return property(getter)

class MyClass(object):
    @MyProperty
    def myFunction(foo):
        return foo

MyClass().myFunction(5.)
>>> 5.0

Это все хорошо, но когда я теперь также передаю функцию вдоль декоратора, как это:

class MyClass(object):
    @MyProperty(int)
    def myFunction(foo):
        return foo

, и я теперь вызываю

MyClass().myFunction(5)
>>> TypeError: 'property' object is not callable 

пока я ожидаю получить int(5) в результате.

1 Ответ

1 голос
/ 17 октября 2019

Когда вы пишете

@MyProperty(int)
def myFunction(foo)
    ...

, это означает, что вызывается MyProperty(int), и все, что возвращается, затем вызывается с myFunction в качестве аргумента. Поэтому MyProperty должна быть функцией, которая возвращает функцию, которая принимает функцию и возвращает функцию.

Таким образом, вы можете написать свой декоратор примерно так:

def MyProperty(optional=None):
    def decorator(func):
        def getter(*args, **kwargs):
            """unspecified magic goes here"""
            return func(*args, **kwargs) if not optional else optional(func(*args, **kwargs))
        return getter
    return decorator

Итак MyProperty(int)возвращает функцию (decorator), а decorator возвращает все, что вы украшаете.

Однако, когда вы вызываете ее без аргумента, вам все равно нужно вызвать ее @MyProperty() вместо @MyProperty, иначе вы пропустите этап распаковки.

>>> class MyClass:
...    @MyProperty()
...    def f1(foo):
...      return foo
...    @MyProperty(int)
...    def f2(foo):
...      return foo
...
>>> MyClass.f1(1.5)
1.5
>>> MyClass.f2(1.5)
1

Я не уверен насчет вашего использования property. Обе ваши функции в этом примере являются просто функциями внутри класса. У них нет аргумента self или cls, и вы вызываете их из самого класса, а не из экземпляра. Несколько неясно, к чему вы стремились.

Когда я попробовал это в Python 2, мне пришлось объявить функции как статические методы, чтобы это работало.

>>> class MyClass(object):
...    @staticmethod
...    @MyProperty()
...    def f1(foo):
...      return foo
...    @staticmethod
...    @MyProperty(int)
...    def f2(foo):
...      return foo
...
>>> MyClass.f1(0.5)
0.5
>>> MyClass.f2(1.5)
1
...