Как работает декоратор @property?

Я хотел бы понять, как работает встроенная функция property. Меня смущает то, что property также можно использовать в качестве декоратора, но он принимает только аргументы при использовании в качестве встроенной функции, а не при использовании в качестве декоратора.

Этот пример приведен из документации:

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

property аргументы getx, setx, delx и строка doc.

В приведенном ниже коде property используется как декоратор. Объектом этого является функция x, но в приведенном выше коде в аргументах нет места для функции объекта.

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

И как создаются декораторы x.setter и x.deleter? Я смущен.

537
задан ashim 26 июня '13 в 23:47
источник поделиться

7 ответов

Функция property() возвращает специальный объект дескриптора :

>>> property()
<property object at 0x10ff07940>

Этот объект имеет дополнительные методы:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

Они также действуют как декораторы. Они возвращают новый объект свойства:

>>> property().getter(None)
<property object at 0x10ff079f0>

который является копией старого объекта, но с заменой одной из функций.

Помните, что синтаксис @decorator - это просто синтаксический сахар; синтаксис:

@property
def foo(self): return self._foo

действительно означает то же самое, что

def foo(self): return self._foo
foo = property(foo)

so foo функция заменяется на property(foo), что мы видели выше, это специальный объект. Затем, когда вы используете @foo.setter(), то, что вы делаете, это вызов метода property().setter, который я показал вам выше, который возвращает новую копию свойства, но на этот раз с заменой функции setter на декорированный метод.

Следующая последовательность также создает свойство full-on, используя эти методы декоратора.

Сначала мы создаем некоторые функции и объект property только с помощью getter:

>>> def getter(self): print 'Get!'
... 
>>> def setter(self, value): print 'Set to {!r}!'.format(value)
... 
>>> def deleter(self): print 'Delete!'
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Далее мы используем метод .setter() для добавления сеттера:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

В последнее время мы добавляем дебетер с помощью метода .deleter():

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

И последнее, но не менее важное: объект property действует как объект дескриптора , поэтому он имеет .__get__(), .__set__() и .__delete__(), чтобы подключиться к атрибуту экземпляра, получить, установить и удалить:

>>> class Foo(object): pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

В дескрипторе Howto используется чистая реализация образца python типа property():

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
631
ответ дан Martijn Pieters 26 июня '13 в 23:54
источник поделиться

Документация говорит, что это просто ярлык для создания свойств readonly. Так

@property
def x(self):
    return self._x

эквивалентно

def getx(self):
    return self._x
x = property(getx)
77
ответ дан J0HN 26 июня '13 в 23:52
источник поделиться

Первая часть проста:

@property
def x(self): ...

совпадает с

def x(self): ...
x = property(x)
  • который, в свою очередь, является упрощенным синтаксисом для создания property только с помощью getter.

Следующим шагом будет расширение этого свойства с помощью setter и deleter. И это происходит с помощью соответствующих методов:

@x.setter
def x(self, value): ...

возвращает новое свойство, которое наследует все от старого x плюс заданный сеттер.

x.deleter работает одинаково.

55
ответ дан glglgl 26 июня '13 в 23:53
источник поделиться

Вот минимальный пример того, как @property можно реализовать:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

В противном случае word остается методом вместо свойства.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'
23
ответ дан AlexG 15 февр. '17 в 3:46
источник поделиться

Это следующее:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

То же, что и:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

То же, что и:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

То же, что и:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

Это то же самое, что:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x
8
ответ дан Bill Moore 24 мая '17 в 21:38
источник поделиться

Свойство может быть объявлено двумя способами.

  • Создание методов getter, setter для атрибута, а затем передача их в качестве аргумента функции свойство
  • Использование декоратора @property.

Вы можете взглянуть на несколько примеров, которые я написал о свойствах в python.

1
ответ дан nvd 13 июля '17 в 12:20
источник поделиться

@property - это декоратор, который преобразует метод в свойство (также называемый атрибутом).

-1
ответ дан Yumi Tada 05 нояб. '17 в 7:45
источник поделиться

Другие вопросы по меткам