Caching attributes in superclass

2019-07-18 05:07发布

I have a class which caches some values to avoid computing them many times, for instance

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

In this way, self._value is computed only once and all the other times the precomputed value is returned. So far so good.

Now, let's suppose I want to subclass A with class B. In our case class B will have its own method of computing self._value but it sometimes will need A's value, like in this example:

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

Now, clearly the trouble is that if get_old_value() is called before value() it will cache A's value forever. If value() is called before get_old_value() in the same way, get_old_value() will actually always return value().

Of course, one could simply use A's <complex code that produces value>, in the implementation of get_old_value() but that would duplicate code (which would pretty much make subclassing useless) or even wrap <complex code that produces value> inside another method in A and call that method in get_old_value() but this would not use caching at all.

Another way could be the following:

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

but that would anyway remove caching for A's version of value and does not look clean at all. Is there any better way to accomplish this?

One thing I want to add is that in my code A and B make really sense as superclass and subclass, otherwise I would consider composition.

1条回答
神经病院院长
2楼-- · 2019-07-18 05:30

What you need to do is use name-mangling -- this will allow each class/subclass to maintain a private version of the variable so they don't clobber each other:

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

And in use:

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

Please note you now need to set __value in B's __init__ as well.

See also this answer for a couple more tidbits about name-mangling.

查看更多
登录 后发表回答