динамически устанавливать свойство экземпляра/запоминаемый атрибут в python?

У меня есть существующий пример класса в Python 2.7x

class Example(object):
    a = None
    b = None
    c = None

и существующий экземпляр

anInstance = Example()
anInstance.a = 100
anInstance.b = 200
anInstance.c = 300

Я провел рефакторинг/чистку кода, и теперь известно, что anInstance.c — дорогостоящая операция, которая редко используется.

в идеальном мире я бы просто сделал это:

class Example(object):
    _c = None
    @property
    def c(self):
        if self._c is not None:
           self._c = DO EXPENSIVE STUFF HERE
        return self._c

Проблема в том, что я не могу изменить class Example прямо сейчас. [Таким образом, быстрое исправление состояло бы в том, чтобы установить его как функцию и изменить каждые obj.c на obj.c()]

Насколько я знаю, я не могу динамически назначать свойство/запоминание, если я не изменю объект. Правильно ли это понимание? Я ожидаю разочарования здесь, я просто хочу подтверждения.


person Jonathan Vanasco    schedule 21.06.2013    source источник
comment
Конечно, если obj.c не является свойством, то оно вычисляется в Example.__init__, и в этом случае вам нужно изменить Example? В противном случае, почему превращение его в свойство что-то изменит?   -  person Katriel    schedule 22.06.2013
comment
Это часть кода из веб-приложения, которое обрабатывает состояние для пользователей. init просто устанавливает значения заполнителей. Состояние условно вычисляется только для нескольких запросов; этот конкретный атрибут требуется еще реже. Я не писал/разрабатывал эту часть. Я пытаюсь это исправить. Как бы мне ни хотелось все это выпотрошить, это не вариант.   -  person Jonathan Vanasco    schedule 22.06.2013


Ответы (1)


Вы можете изменить классы Python постфактум:

@property
def c(self):
    if self._c is None:
        self._c = DO EXPENSIVE STUFF HERE
    return self._c

Example.c = c
Example._c = None

Теперь вы добавили в свой класс свойство c, а также добавили атрибут _c.

Конечно, вам может потребоваться переопределить любые существующие методы класса, которые предполагают, что они могут назначать self.c.

Процесс динамического добавления или замены атрибутов объектов часто называют исправлением Monkey Patch.

person Martijn Pieters    schedule 21.06.2013
comment
Конечно, если можно изменить код, создающий экземпляр Example, было бы лучше наследовать от Example и вместо этого создавать экземпляры подкласса. - person Marcin; 22.06.2013
comment
@Marcin: в этот момент вам нужно исправить код, который импортирует Example. - person Martijn Pieters; 22.06.2013
comment
Нет, если вы можете изменить его без обезьяньих патчей. И если есть ограниченное количество мест, где создаются Example экземпляры, то достаточно просто изменить их. - person Marcin; 22.06.2013
comment
Я не могу пропатчить Пример. Мне конкретно нужно повлиять на instance , а не на class. В приложении класс Example используется в нескольких местах для хранения битов конфигурации и данных состояния. Обезьянье исправление класса потенциально может причинить больше вреда/работы, чем корректировка кода, унаследованного от примера. Единственный реальный способ сделать это с помощью Monkeypatching — это переопределить Example.__getattr__, чего я бы предпочел не делать. - person Jonathan Vanasco; 22.06.2013
comment
В этом случае вы застряли, потому что вы не можете добавлять свойства или хук __getattr__ к экземплярам. В качестве дескрипторов свойства вызываются правильно только при поиске в классе, а ловушки dunder, такие как __getattr__, только ищутся в типе (так что классы для экземпляров). - person Martijn Pieters; 22.06.2013