Python中pickle为何序列化cached_property?如何阻止该行为?
问题解答
为什么pickle会序列化cached_property的缓存值
functools.cached_property(以及大多数同类型实现)的核心逻辑是:第一次访问属性时执行计算方法,随后将结果存入实例的__dict__字典(键即为属性名),后续访问直接从__dict__取值以跳过重复计算。
而pickle序列化实例的默认行为,是把实例__dict__中的所有键值对全部打包序列化。因此,只要该缓存属性已经被计算过(即__dict__中存在对应键),就会被一同序列化。
另外,标准cached_property是只读属性,未实现setter方法,所以你无法通过instance.attr = None的方式手动清除缓存,这就是你无法重置它的原因。
如何阻止pickle序列化该缓存属性
最可靠的方式是利用pickle的钩子方法__getstate__和__setstate__,手动控制序列化与反序列化的内容:
实现示例
from functools import cached_property import pickle class MyClass: def __init__(self, value): self.value = value @cached_property def computed(self): print("执行计算逻辑...") return self.value * 2 def __getstate__(self): # 复制实例状态字典,移除缓存属性对应的键 state = self.__dict__.copy() state.pop('computed', None) return state def __setstate__(self, state): # 恢复实例基础状态 self.__dict__.update(state) # 无需恢复缓存,下次访问computed时会重新触发计算
验证效果
# 创建实例并触发缓存 obj = MyClass(5) print(obj.computed) # 输出:执行计算逻辑... 10 # 序列化 pickled = pickle.dumps(obj) # 反序列化 new_obj = pickle.loads(pickled) print(new_obj.computed) # 输出:执行计算逻辑... 10(再次触发计算,说明缓存未被序列化)
原理说明
__getstate__会在pickle序列化前被调用,返回的字典就是实际要序列化的内容。通过复制__dict__并移除缓存属性的键,让pickle不包含缓存值。__setstate__在反序列化时被调用,负责恢复实例状态。这里仅恢复基础属性,缓存属性会在第一次访问时重新计算。
如果类中有多个cached_property,只需在__getstate__中把所有对应缓存键逐一pop掉即可。
内容的提问来源于stack exchange,提问作者user107511
相关产品推荐
相关产品推荐

