在超类中缓存属性

时间:2017-03-20 21:24:31

标签: python-3.x inheritance caching properties

我有一个类来缓存一些值,以避免多次计算它们,例如

class A(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b
        self._value = None

    @property
    def value(self):
        if self._value is None:
            self._value = # <complex code that produces value>
        return self._value

通过这种方式,self._value只计算一次,并且所有其他时间都会返回预先计算的值。到目前为止一切都很好。

现在,假设我想将A与类B子类化。在我们的案例中,类B将有自己的计算方法self._value但有时需要A value,如下例所示:

class B(A):

    def __init__(self, a, b):
        super().__init__(a, b)

    @property
    def value(self):
        if self._value is not None:
            self._value = # <complex code that produces B's version of value>
        return self._value

    def get_old_value(self):
        return super().value  # here comes the trouble

现在,显然问题是如果在get_old_value()之前调用value(),它将永久缓存A value。如果在value()之前以相同的方式调用get_old_value()get_old_value()实际上将始终返回value()

当然,在A的实现中,可以简单地使用<complex code that produces value>&#39; get_old_value(),但这会重复代码(这会使子类化无用)或者甚至将<complex code that produces value>包装在A中的另一个方法中,并在get_old_value()中调用该方法,但这根本不会使用缓存。

另一种方式可能如下:

def get_old_value(self):
    result = super().value
    self._c = None
    return result

但是,无论如何都会删除A版本的value的缓存,看起来并不干净。有没有更好的方法来实现这个目标?

我想补充的一点是,在我的代码中AB真正意义上的超类和子类,否则我会考虑组合。

1 个答案:

答案 0 :(得分:2)

你需要做的是使用名称修改 - 这将允许每个类/子类维护变量的私有版本,以便它们不会互相破坏:

class A(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.__value = None

    @property
    def value(self):
        if self.__value is None:
            self.__value = 7
        return self.__value

class B(A):

    def __init__(self, a, b):
        super().__init__(a, b)
        self.__value = None

    @property
    def value(self):
        if self.__value is None:
            self.__value = 17
        return self.__value

    def get_old_value(self):
        return super().value  # no more trouble here

并在使用中:

>>> b = B(1, 2)
>>> print(b.value)
17
>>> print(b.get_old_value())
7

请注意,您现在也需要在__value&#39 {s} B设置__init__

另请参阅this answer了解更多关于名称修改的花絮。