You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:40:27