如何在Circle属性直接修改时更新AllCircles的cached_property缓存?
解决
@cached_property在关联属性修改时的缓存更新问题 核心问题在于functools.cached_property仅会在缓存键被主动删除时失效,而直接修改内部Circle实例的radius属性时,AllCircles无法感知到这个变化,导致缓存不会自动更新。下面提供两种实用解决方案:
方案一:让Circle支持属性变更通知(主动触发缓存清除)
通过给Circle添加回调机制,当radius被修改时主动通知AllCircles清除缓存。这种方式性能最优,适合需要频繁修改关联属性的场景。
修改后的代码
from functools import cached_property class Circle: def __init__(self, radius: float): self._radius = radius self._radius_change_callbacks = [] # 存储属性变更回调 @property def radius(self): return self._radius @radius.setter def radius(self, value): if self._radius != value: self._radius = value # 触发所有注册的回调函数 for callback in self._radius_change_callbacks: callback() def add_radius_change_callback(self, callback): self._radius_change_callbacks.append(callback) class AllCircles: def __init__(self, circles): self._circles = tuple(circles) self._register_circle_callbacks() def _register_circle_callbacks(self): # 给每个Circle注册清除max_radius缓存的回调 for circle in self._circles: circle.add_radius_change_callback(lambda: self.__dict__.pop('max_radius', None)) @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 = tuple(value) self._register_circle_callbacks() # 给新的Circle实例注册回调 @cached_property def max_radius(self): print("Compute cached property radius") return max(c.radius for c in self.circles)
测试验证
my_circles = AllCircles([Circle(4), Circle(3)]) print(my_circles.max_radius) # 输出4,打印计算日志 my_circles.circles[0].radius = 9 print(my_circles.max_radius) # 输出9,重新计算缓存
方案二:自定义缓存校验逻辑(被动检查依赖变化)
如果无法修改Circle类,可以通过在AllCircles中记录依赖属性的哈希值,每次访问缓存前检查哈希是否变化,以此判断是否需要清除缓存。这种方式无需修改依赖类,但每次访问都会有额外的哈希计算开销,适合关联对象数量较少的场景。
修改后的代码
from functools import cached_property class Circle(): def __init__(self, radius: float): self.radius = radius class AllCircles(): def __init__(self, circles): self._circles = tuple(circles) self._last_radii_hash = None # 记录上次计算时的radius哈希值 @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._last_radii_hash = None self._circles = tuple(value) @cached_property def _max_radius(self): print("Compute cached property radius") max_val = max(c.radius for c in self.circles) # 更新当前radius的哈希记录 self._last_radii_hash = hash(tuple(c.radius for c in self.circles)) return max_val @property def max_radius(self): # 检查当前所有radius的哈希值是否变化 current_hash = hash(tuple(c.radius for c in self.circles)) if current_hash != self._last_radii_hash: # 哈希变化,清除缓存并更新记录 self.__dict__.pop('_max_radius', None) self._last_radii_hash = current_hash return self._max_radius
测试验证
my_circles = AllCircles([Circle(5), Circle(0)]) print(my_circles.max_radius) # 输出5,打印计算日志 my_circles.circles[0].radius = 9 print(my_circles.max_radius) # 输出9,重新计算缓存
内容的提问来源于stack exchange,提问作者Clej
相关产品推荐
相关产品推荐

