Как работают свойства Python?

Я успешно использовал свойства Python, но я не вижу, как они могут работать. Если я разыскиваю свойство вне класса, я просто получаю объект типа property:

@property
def hello(): return "Hello, world!"

hello  # <property object at 0x9870a8>

Но если я помещаю свойство в класс, поведение очень отличается:

class Foo(object):
   @property
   def hello(self): return "Hello, world!"

Foo().hello # 'Hello, world!'

Я заметил, что unbound Foo.hello все еще является объектом property, поэтому создание экземпляра должно делать магию, но что это за магия?

Ответ 1

Как отметили другие, они используют функцию языка, называемую дескрипторами.

Причина того, что реальный объект свойства возвращается при доступе к нему через класс Foo.hello заключается в том, что свойство реализует специальный метод __get__(self, instance, owner):

  • Если к экземпляру обращаются к экземпляру, то этот экземпляр передается как соответствующий аргумент, а owner является класс этого экземпляра.
  • Когда к нему обращаются через класс, instance имеет значение None, и передается только owner. Объект property распознает это и возвращает self.

Помимо руководства по дескрипторам, см. Также документацию по реализации дескрипторов и вызову дескрипторов в руководстве по языку.

Ответ 2

Для корректной работы @properties класс должен быть подклассом объекта. когда класс не является подклассом объекта, то при первом попытке доступа к установщику он фактически создает новый атрибут с более коротким именем вместо доступа через сеттер.

Ниже выполняется не.

class C(): # <-- Notice that object is missing

    def __init__(self):
        self._x = None

    @property
    def x(self):
        print 'getting value of x'
        return self._x

    @x.setter
    def x(self, x):
        print 'setting value of x'
        self._x = x

>>> c = C()
>>> c.x = 1
>>> print c.x, c._x
1 0

Следующее будет работать правильно

class C(object):

    def __init__(self):
        self._x = None

    @property
    def x(self):
        print 'getting value of x'
        return self._x

    @x.setter
    def x(self, x):
        print 'setting value of x'
        self._x = x

>>> c = C()
>>> c.x = 1
setting value of x
>>> print c.x, c._x
getting value of x
1 1

Ответ 3

Свойства дескрипторы, и дескрипторы ведут себя особенно в случае члена экземпляра класса. Короче говоря, если a - это экземпляр типа a, а A.foo - дескриптор, то A.foo эквивалентен A.foo.__get__(a).