I'll try to take a dummy example to explain my goal: updating a cache (defined with @cached_property) computed from an instance attribute. Let's say I have an object AllCircles that is composed of a tuple of Circles. Each Circle is defined with its radius. I want to cache the value of the biggest radius as a property of AllCircles. Here's what I did: from functools import cached_property class Circle(): def __init__(self, radius: float): self.radius = radius class AllCircles(): def __init__(self, circles: (Circle)): self._circles = tuple(circles) @property def circles(self): print("Call getter") return self._circles @circles.setter def circles(self, value): print("Call setter") self.__dict__.pop('max_radius', None) self._circles = value @cached_property def max_radius(self): print("Compute cached property radius") return max([_.radius for _ in self.circles]) I can define an object AllCircles from a set of Circles, and call max_radius to get the biggest value. > my_circles = AllCircles([Circle(4), Circle(3)]) > my_circles.max_radius Compute cached property radius Call getter 4 Which is ok. If I call the function again, I get: > my_circles.max_radius 4 as nothing's printed, I guess the cache is called, that's ok. If I set the circles: > my_circles.circles = [Circle(5), Circle(0)] > my_circles.max_radius Call setter Compute cached property radius Call getter 5 I again get the right value, because setting a new object circles calls the setter, and its self.__dict__.pop()method empties the cache. However, if I directly modify the radius of a circle, I don't empty my cache: > my_circles.circles[0].radius = 9 > my_circles.max_radius Call getter 5 # instead of 9 How can I change my code to update my cache when an attribute is modified directly ? Continue reading...