575

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

Этот пример из documentation:

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 аргументы «s являются getx, setx, delx и строка документации.

В нижеследующем коде 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 декораторы? Я смущен.

+3

См. Также: [Как работают свойства Python?] (Http://stackoverflow.com/q/6193556/562769) –

+1

'property' - фактически класс (а не функция), хотя он, вероятно, вызывает вызов '__init __()' метод, когда вы делаете объект, конечно. Использование 'help (property)' из терминала проницательно. По какой-то причине 'help' также является классом. – Shule

ответ

661

property() функция возвращает специальный descriptor object:

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

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

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

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

>>> 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 с .deleter()

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

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

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

Дескриптор Howto включает в себя pure python sample implementation из 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__) 
+8

Очень хорошо. Вы можете добавить тот факт, что после 'Foo.prop = prop' вы можете сделать' Foo(). Prop = 5; pront Foo(). prop; del Foo(). prop' с желаемым результатом. – glglgl

+0

Почему 'property(). Getter' и' property(). Deleter' имеют одинаковый адрес, но 'property(). Setter' does not? – gerrit

+8

Объекты метода создаются «на лету» и * могут * повторно использовать одно и то же место памяти, если оно доступно. –

86

Documentation says это просто ярлык для создания свойств readonly. Так

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

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

def getx(self): 
    return self._x 
x = property(getx) 
59

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

@property 
def x(self): ... 

такая же, как

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

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

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

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

x.deleter работает так же.

34

Вот минимальный пример того, как @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' 
+1

Как будет выглядеть этот пример, если функция/свойство word() необходимо определить в __init__? –

+0

Может кто-то угодить объясните, почему я создал бы здесь декоратор свойств, вместо того, чтобы просто иметь «self.word = my_word», который затем работал бы так же, как и «print» (Thing («ok»). word) = 'ok'' – SilverSlash

+1

@SilverSlash Это просто простой пример: реальный случай использования будет включать более сложный метод. – AlexG

15

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

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 
2

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^