如果属性更改导致属性过多,是否会引发标志



对于我的应用程序,我有一个对象,它有一些属性集。用户可以访问和调整一组属性,即参数。另一组属性,即输出,应该可以由用户访问,但使用内部方法进行计算。此外,如果调整了任何参数属性,则还必须根据内部方法重新计算输出并进行相应调整。然而,由于这些计算可能成本高昂,我不想不必要地运行它们,除非(或直到(提出要求。

目前,我可以通过使每个"参数"属性成为一个属性并包括一个self.calculated标志来实现这一点,该标志在任何参数更改时都会升起,还可以使每个"输出"属性成为检查self.calculated标志的属性,因此,如果不需要计算,则直接返回输出,或者执行计算,降低标志,并返回输出。

参见代码

class Rectangle(object):
def __init__(self, length=1, width=1):
self._length = length
self._width = width
self._area = self.calc_area()
self._perim = self.calc_perim()
self.calculated = True
@property
def length(self):
return self._length
@length.setter
def length(self, value):
if value != self._length:
self._length = value
self.calculated = False
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if value != self._width:
self._width = value
self.calculated = False
@property
def area(self):
if self.calculated is True:
return self._area
else:
self.recalculate()
return self._area
@property
def perim(self):
if self.calculated is True:
return self._perim
else:
self.recalculate()
return self._perim
def calc_area(self):
return self.length * self.width
def calc_perim(self):
return 2 * (self.length + self.width)
def recalculate(self):
self._area = self.calc_area()
self._perim = self.calc_perim()
self.calculated = True
def double_width(self):
self.width = 2 * self.width

这给出了所需的行为,但似乎是属性的过度增殖,如果必须有大量的参数和输出,这将是特别有问题的。

有没有更干净的方法来实现这种属性更改/重新计算结构?我发现了几篇文章,其中提出了一个解决方案,涉及为类编写__setattr__方法,但我不确定这是否可以直接在我的中实现,因为行为应该根据设置的特定属性而有所不同。我想这可以通过在__setattr__方法中检查属性是参数还是输出来处理。。。

装饰类以监视属性更改

如何识别属性';正在设置的属性?

有几个不同的选项,使用哪个选项在很大程度上取决于用例。显然,在许多用例中,下面的选项可能非常糟糕。

输入改变时删除输出

实现此功能的示例:

@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)值,而不是在每个实例中复制它。因此,对于某些用例,这可能更有效。

请注意,您的问题最终源于您正试图进行一些内存化(您希望保存计算结果,这样当有人访问对象的输出时,如果已经为当前输入计算了输出,则不必计算输出(,但可以说,您需要跟踪何时"使缓存无效"。如果您只是将面积和周长视为方法而非属性,或者将实例视为不可变的,并要求通过使用新值创建新实例来重置属性,则可以消除添加到长度和宽度中的复杂性。你不可能拥有全部:如果没有一些开销,你就不可能从可变属性中缓存值。

PSis Trueif self.calculated is True:中是冗余的。

最新更新