Полагаю, мне следует расширить этот ответ, теперь, когда я стал старше и мудрее и знаю, что происходит. Лучше поздно, чем никогда.
Вы можете динамически добавлять свойство в класс. Но в этом-то и суть: вы должны добавить его к классу .
>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
A property
на самом деле является простой реализацией вещи, называемой дескриптором . Это объект, который обеспечивает пользовательскую обработку для данного атрибута, для данного класса . Вроде как способ выделить огромное if
дерево из __getattribute__
.
Когда я запрашиваю foo.b
в приведенном выше примере, Python видит, что b
, определенный в классе, реализует протокол дескриптора - что просто означает, что это объект с __get__
, __set__
или __delete__
метод. Дескриптор несет ответственность за обработку этого атрибута, поэтому Python вызывает Foo.b.__get__(foo, Foo)
, и возвращаемое значение передается вам в качестве значения атрибута. В случае property
каждый из этих методов просто вызывает fget
, fset
или fdel
, которые вы передали конструктору property
.
Дескрипторы - это действительно способ Python раскрыть всю реализацию OO. На самом деле, существует другой тип дескриптора, даже более распространенный, чем property
.
>>> class Foo(object):
... def bar(self):
... pass
...
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>
Скромный метод - это просто другой вид дескриптора. __get__
указывает на вызывающий экземпляр в качестве первого аргумента; по сути, он делает это:
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
В любом случае, я подозреваю, что именно поэтому дескрипторы работают только с классами: они являются формализацией того, что обеспечивает классы в первую очередь. Они даже являются исключением из правила: очевидно, вы можете назначать дескрипторы классу, а сами классы являются экземплярами type
! На самом деле, попытка прочитать Foo.b
все еще вызывает property.__get__
; для дескрипторов идиоматично возвращать себя, когда к ним обращаются как к атрибутам класса.
Я думаю, это круто, что практически вся ОО-система Python может быть выражена в Python. :)
О, и я написал многословное сообщение в блоге о дескрипторах некоторое время назад, если вам интересно.