Существует несколько различных опций, и их использование сильно зависит от варианта использования. Очевидно, что приведенные ниже варианты могут быть довольно плохими во многих случаях использования.
Удаление выходов при изменении входов
Пример реализации этого:
@width.setter
def width(self, value):
if value != self._width:
self._width = value
(self._area,self._perim) = (None,None)
def perim(self):
if not self._perim:
self._perim = calc_perim(self)
return self._perim
Это не решает большинство ваших проблем, но избавляет от флага calculated
(и хотя ваш код пересчитывает все выходные данные, когда любой из них запрашивается после обновления, этот код просто вычисляет запрошенный один).
Обновление значений при изменении входных данных
Когда вы увеличиваете ширину на x, периметр увеличивается на 2 * x, а площадь увеличивается на x * длину. В некоторых случаях применение таких формул для обновления значений при изменении входных данных может быть более эффективным, чем вычисление выходных данных с нуля при каждом изменении входных данных.
Отслеживать последние значения
Всякий раз, когда вы рассчитываете выходные данные, следите не только за тем, что вы получили, но и за тем, какие данные вы использовали для их расчета. Затем в следующий раз, когда у объекта спрашивают, каковы его выходные данные, он может проверить, были ли выходные данные рассчитаны в соответствии с его текущими атрибутами. Очевидно, что это требует умножения входного пространства хранения.
Памятка
Пойдя даже дальше, чем предыдущий параметр, создайте словарь, в котором ключи являются кортежами атрибутов, а значения выводятся. Если у вас есть функция calculate_output(attributes)
, замените все вызовы этой функции на
def output_lookup(attributes):
if not attributes in output_dict.keys():
output_dict[attributes] = calculate_output(attributes)
return output_dict[attributes]
Вам следует использовать эту опцию, если вы ожидаете, что определенные комбинации атрибутов будут часто повторяться, вычисление выходных данных является дорогостоящим и / или память является дешевой. Это может быть общим для всего класса, поэтому, если у вас есть несколько экземпляров прямоугольников, имеющих одинаковую длину и ширину, вы можете сохранить одно значение (_perim,_area)
вместо дублирования его для каждого экземпляра. Так что для некоторых вариантов использования это может быть более эффективным.
Обратите внимание, что ваша проблема в конечном итоге связана с тем фактом, что вы пытаетесь задействовать некоторое запоминание (вы хотите сохранить результаты из своих вычислений, чтобы при обращении к выходным данным объекта вы не Вы должны рассчитать выходные данные, если они уже были рассчитаны для текущих входных данных), но вам необходимо отслеживать, когда, так сказать, «сделать кэш недействительным». Если бы вы просто рассматривали область и периметр как методы, а не атрибуты, или вы должны были рассматривать экземпляры как неизменяемые и требовать сброса атрибутов путем создания нового экземпляра с новыми значениями, вы бы исключили сложность, которую вы добавили на длину и ширину. Вы не можете иметь все это: вы не можете иметь кэшированные значения из изменяемых атрибутов без некоторых накладных расходов.
PS is True
является избыточным в if self.calculated is True:
.