lru缓存失效主因是参数不可哈希(如list、dict),导致跳过缓存且不报错;maxsize=none不限数量但有内存风险;需用cache_clear()显式清缓存;默认参数为可变对象会破坏键一致性。

为什么 lru_cache 有时没生效?
最常见原因是被装饰的函数接收了不可哈希的参数,比如 list、dict 或自定义对象。一旦参数无法被哈希,lru_cache 会直接跳过缓存,每次调用都重新执行函数,且不报错——这容易让人误以为“缓存开了但没用”。
实操建议:
- 检查函数所有参数类型,确保全是不可变类型(
int、str、tuple、frozenset等) - 若必须传
list,可临时转成tuple:例如@lru_cache装饰的函数内不直接接受data: list,而是定义为def f(data_tuple: tuple),调用时传f(tuple(my_list)) - 自定义类需实现
__hash__和__eq__,且保证实例不可变
maxsize 设为 None 和 128 有什么实际区别?
默认 maxsize=128 表示最多缓存最近 128 次不同参数组合的结果;设为 None 则不限数量,但内存占用随调用多样性线性增长,可能引发内存泄漏。
使用场景与权衡:
- 纯计算函数(如递归斐波那契、解析固定配置字符串),参数空间小 → 可设
maxsize=None - 带用户输入或时间戳参数的函数 → 必须限制
maxsize,否则缓存键无限膨胀 - 调试时可先设
maxsize=1,配合cache_info()观察命中率:my_func.cache_info()返回CacheInfo(hits=..., misses=..., maxsize=..., currsize=...)
如何安全地清除缓存或在测试中重置?
lru_cache 是函数级绑定的,没有全局开关。清除操作必须显式调用对应函数的 cache_clear() 方法。
常见误操作和正确做法:
- 错误:试图通过
del my_func.cache或重赋值清空 —— 缓存逻辑在装饰器闭包里,无法外部访问 - 正确:调用
my_func.cache_clear(),例如在单元测试setUp中重置状态 - 注意:如果函数被多次装饰(比如套了两层
@lru_cache),只有最外层的cache_clear()有效 - 多线程下
cache_clear()是线程安全的,但缓存命中/未命中统计(cache_info)不是原子的,生产环境避免高频读取该统计
为什么带默认参数的函数缓存行为容易出人意料?
Python 中默认参数在函数定义时求值一次,而 lru_cache 的键生成依赖参数值本身。如果默认参数是可变对象(如 default=[]),后续调用即使不传参,也会因该列表被修改导致缓存键“看似相同实则不同”。
例如:
@lru_cache
def process(items=[]):
items.append(1)
return len(items)
第一次调用 process() 返回 1,第二次返回 2——因为 items 是同一个 list 对象,缓存键始终是 ([],),但值已变。这不是缓存 bug,而是默认参数陷阱被放大了。
规避方式:
- 默认参数一律用
None,内部初始化:def process(items=None): items = items or [] - 若必须用可变默认值,且需要缓存,应在函数入口做深拷贝或转不可变结构(如
tuple(items))再参与缓存键计算
缓存真正起效的前提,是“相同输入”能稳定生成“相同哈希键”,而默认参数的隐式共享常常悄悄破坏这一点。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











