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.
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:
And in use:
Please note you now need to set
__value
inB
's__init__
as well.See also this answer for a couple more tidbits about name-mangling.