1

This is a followup to this question. I have a class which caches an attribute for its subclasses:

class A(object):

    def __init__(self):
        self._value = None

    @property
    def value(self):
        if self._value is None:
            self._value = self.complex_computation()
        return self._value

    def complex_computation(self):
        raise NotImplemented

Now, let's suppose I have many subclasses of A, each one overriding only complex_computation(), hence delegating the caching mechanism to the parent class. Now let's suppose one of this classes is B and B has a subclass itself, C:

class B(A):
    def complex_computation(self):
        return # B's complex computation happens here

class C(B):
    def complex_computation(self):
        return # C's complex computation happens here

However, let's assume that C also wants to return B's value still making use of the caching mechanism defined in class A. C could add the following method:

def parent_complex_computation(self):
    return super().value  # this does not work

As described in the previous question, this would not work. In fact, if one accesses C's value first, then parent_complex_computation() would always return that version. Same problem happens when parent_complex_computation() is used first, caching B's value.

As answered in the previous question, name mangling (i.e. using self.__value instead of self._value in A) could solve the problem, but in that case every subclass of A should redefine both the caching mechanism as its own property and the mangled attribute. Is there a better way to keep the caching mechanism only in class A and not duplicating code, while allowing a subclass to both override the cached method complex_computation AND call the parent's cached value, hence caching both values?

Community
  • 1
  • 1
Simone Bronzini
  • 1,057
  • 1
  • 12
  • 23

1 Answers1

0

You have a couple options:

  • redesign __init__ to figure out the correct name-mangled version of the name so it is always appropriate for the class it is in

  • move the caching behavior into the complex_computation() method

I'll show the second method:

def complex_computation(self, _cache=[]):
    if not _cache:
        # calculate
        # ...
        # calculated_value = ...
        # 
        # and save
        _cache.append(calculated_value)

    return _cache[0]

And then when you want to see your immediate parent's value:

def get_parent_calculation(self):
    return super().complex_computation()
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • But that would actually mean that the caching mechanism is reimplemented in every subclass of `A` which is someting I want to avoid as it would annihilate the purpose of having a superclass taking care of the caching logic. – Simone Bronzini Mar 21 '17 at 17:25
  • @SimoneBronzini: If you have a chain of classes `A` -> `B` -> `C` -> `D` -> `E` -- how far back do you want to be able to call an ancestor's `complex_method()`? – Ethan Furman Mar 22 '17 at 04:20
  • a child class should be able to call the parent's `complex_computation()`, no further than that – Simone Bronzini Mar 22 '17 at 10:05