Динамическое добавление @property в Python - PullRequest
18 голосов
/ 02 июня 2010

Я знаю, что могу динамически добавлять метод экземпляра к объекту, делая что-то вроде:

import types
def my_method(self):
    # logic of method
# ...
# instance is some instance of some class
instance.my_method = types.MethodType(my_method, instance)

Позже я могу позвонить instance.my_method(), и я буду связан правильно, и все будет работать.

Теперь мой вопрос: как сделать то же самое, чтобы получить поведение, которое даст украшение нового метода с помощью @property?

Я бы предположил что-то вроде:

instance.my_method = types.MethodType(my_method, instance)
instance.my_method = property(instance.my_method)

Но при этом instance.my_method возвращает объект свойства.

Ответы [ 2 ]

35 голосов
/ 02 июня 2010

Объекты дескриптора property должны жить в классе , , а не в экземпляре , чтобы получить желаемый эффект. Если вы не хотите изменять существующий класс, чтобы избежать изменения поведения других экземпляров, вам необходимо создать «класс для каждого экземпляра», например ::

def addprop(inst, name, method):
  cls = type(inst)
  if not hasattr(cls, '__perinstance'):
    cls = type(cls.__name__, (cls,), {})
    cls.__perinstance = True
    inst.__class__ = cls
  setattr(cls, name, property(method))

Я отмечаю эти специальные классы для каждого экземпляра атрибутом, чтобы избежать ненужного создания нескольких, если вы выполняете несколько вызовов addprop для одного экземпляра.

Обратите внимание, что, как и для других случаев использования property, вам необходимо, чтобы класс в игре был новый стиль (обычно получаемый прямым или косвенным наследованием от object), не древний унаследованный стиль (удален в Python 3), который по умолчанию назначен классу без оснований.

3 голосов
/ 18 июня 2013

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

cls = type(my_instance)
cls.my_prop = property(lambda self: "hello world")
print(my_instance.my_prop)
# >>> hello world

Примечание: Добавление другого ответа, потому что я думаю, что @Alex Martelli, хотя и правильно, достигает желаемого результата, создав новый класс, который содержит свойство, но этот ответ должен быть более прямым / простым абстрагирование того, что происходит в его собственном методе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...