Подтвердить что ты не робот

Динамическое добавление @property в python

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

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(), а self будет правильно привязан и все будет работать.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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