我将尝试采用一个虚拟示例来解释我的目标:更新从实例属性计算的缓存(使用@cached_property定义)。
假设我有一个对象AllCircles,它是由一组circle组成的。每个圆都有它的半径。我想缓存最大半径的值作为AllCircles的一个属性。我是这样做的:
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])
我可以从一组圆圈中定义一个对象AllCircles,并调用max_radius来获得最大的值。
> my_circles = AllCircles([Circle(4), Circle(3)])
> my_circles.max_radius
Compute cached property radius
Call getter
4
这是好的。如果我再次调用这个函数,我得到:
> my_circles.max_radius
4
因为没有打印,我猜缓存被调用,没关系。如果我设置圆圈:
> my_circles.circles = [Circle(5), Circle(0)]
> my_circles.max_radius
Call setter
Compute cached property radius
Call getter
5
我再次得到正确的值,因为设置一个新对象circles
调用setter,并且它的self.__dict__.pop()
方法清空缓存。
但是,如果我直接修改圆的半径,我不会清空缓存:
> my_circles.circles[0].radius = 9
> my_circles.max_radius
Call getter
5 # instead of 9
我如何改变我的代码来更新我的缓存时,一个属性被直接修改?
在某种程度上,你的要求对我来说似乎有点不自然。实际上,当您设置my_circles.circles[0].radius = 9
时,您不会修改实例my_circles
的任何内容,因此它没有自然的方法知道它应该有不同的行为。此外,可怜的小Circles
不知道他们是更大的东西的一部分,所以他们不能告诉my_circles
。
我猜你可以解决你的问题的一种方法是使半径的一部分包含在my_circles
的信息(或者更一般地说,直接通知Circles
,他们不再孤单通过连接到my_circles
)。
另一种方法是强制my_circles
对每个max_radius
请求进行检查。在这里它可能是愚蠢的,因为它相当于重新计算最大值,但在更复杂的操作中,它可能只是检查一些哈希值。
我不知道这是否有帮助,也许还有其他更好的方法来思考这个问题。