假设我有一个存储一些统计数据的类Foo
,我想使用Python属性封装对这些数据的访问。这是特别有用的,例如,当我只存储变量的方差并希望能够访问其标准偏差时:在这种情况下,我可以定义属性Foo.std
并使其返回平方根方差。
这种方法的问题是,如果我需要多次访问Foo.std
,我将每次计算平方根;此外,由于属性的符号与属性的符号完全相同,因此每次访问属性时,我的类的用户可能都不会意识到正在进行计算。
此示例中的一个替代方案是每次更新方差时计算标准偏差,并将其设置为属性。但是,如果我不需要在每次更新时访问它,那将是低效的。
我的问题是:当您需要执行昂贵的计算时,有效使用Python属性的最佳方法是什么?我应该在第一次调用后缓存结果并在更新时删除缓存吗?或者我不应该使用属性并使用方法Foo.get_std()
代替?
答案 0 :(得分:7)
通常您可以通过缓存来完成此操作。例如,你可以写:
class Foo:
def __int__(self, also, other, arguments):
# ...
self._std = None
@property
def std(self):
if self._std is None:
# ... calculate standard deviation
self._std = ...
return self._std
def alter_state(self, some, arguments):
# update state
self._std = None
所以我们这里有一个属性std
,但也有一个属性_std
。如果尚未计算标准偏差,或者您更改了对象状态以使标准偏差可能已更改,我们会将_std
设置为None
。现在,如果我们访问.std
,我们首先检查_std
是否为None
。如果是这种情况,我们计算标准偏差并将其存储到_std
并返回。这样 - 如果对象没有改变 - 我们以后可以简单地检索它。
如果我们更改对象以使标准偏差可能已更改,我们会将_std
设置回None
,以便在我们再次访问.std
时强制重新评估。
如果我们在重新计算标准差之前两次更改Foo
对象的状态,我们只会重新计算一次。因此,您可以经常更改Foo
对象,使用(接近)无需额外费用(将self._std
设置为None
除外)。因此,如果您有一个庞大的数据集并且您进行了大量更新,那么您只需在实际需要时再努力计算标准偏差。
此外,如果(非常)便宜,这也可以成为更新统计指标的机会。比如说,您有一个经常批量更新的对象列表。如果使用常量递增所有元素,则 mean 也将随该常量递增。因此,改变状态以便可以轻松更改某些指标的功能可能会更新指标,而不是制作这些None
。
请注意,.std
是属性还是函数无关紧要。用户不必知道必须经常计算的频率。 std()
函数将保证一旦计算,第二次检索将非常快。