lru_cache不生效最常见原因是参数不可哈希,如list、dict等,导致缓存完全跳过且不报错;其次为可变默认参数、实例方法绑定self、LRU容量淘汰或手动清空。

为什么 lru_cache 有时不生效?
最常见原因是被装饰的函数接收了不可哈希的参数,比如 dict、list 或自定义类实例。一旦参数无法被哈希,lru_cache 就会直接跳过缓存逻辑,每次调用都重新执行函数,且不报错——你只会发现性能没提升。
- 检查函数参数类型:只允许
str、int、tuple(且内部元素也必须可哈希)、None等内置不可变类型 - 如果必须传
dict,先转成tuple(sorted(d.items()));传list可转为tuple(my_list) - 避免在装饰器里用
maxsize=None却处理大量不同参数组合,内存可能悄悄涨满
如何控制缓存大小和清除策略?
lru_cache 默认只缓存前 128 个调用结果,超出后按 LRU(最近最少使用)淘汰。这不是“固定大小”,而是“最多缓存多少种参数组合”。
- 设
@lru_cache(maxsize=128):最多存 128 组不同参数对应的返回值 - 设
@lru_cache(maxsize=None):不限数量,但注意内存占用和键哈希开销 - 运行时调用
my_func.cache_clear()可手动清空缓存,适合配置变更或测试重置 - 调用
my_func.cache_info()返回CacheInfo(hits=..., misses=..., maxsize=..., currsize=...),是唯一能确认缓存是否真在工作的依据
带默认参数的函数会被正确缓存吗?
会,但要注意 Python 函数调用时的参数绑定方式。默认参数值在定义时就被固化,lru_cache 缓存的是「实际传入的参数」,不是「签名」。
@lru_cache
def calc(x, y=10):
return x * y
<p>calc(5) # 参数实际是 (5, 10),缓存键为 (5, 10)
calc(5, 10) # 同样是 (5, 10),命中缓存
calc(5, 20) # 参数是 (5, 20),另存一份
- 显式传参和用默认值,在缓存系统里是两套键,哪怕结果一样
- 如果函数内部修改了可变默认参数(如
def f(x, lst=[])),缓存行为会更难预测,应避免 - 建议对高频调用路径,统一用显式参数调用,减少键碎片
为什么多线程下 lru_cache 仍安全?
因为 lru_cache 内部用了线程锁保护缓存字典的读写,所有操作都是原子的。但要注意:它不解决函数本身的线程安全问题。
立即学习“Python免费学习笔记(深入)”;
- 缓存命中时不会重复执行函数体,所以若原函数有副作用(如写文件、改全局变量),这些副作用也不会重复发生
- 但如果函数本身依赖外部状态(如读取某个随时变化的全局计数器),缓存结果就可能过期,这时需要自己加版本号或时间戳参数来强制更新键
- 不要指望
lru_cache替代真正的并发控制——它只是让“相同输入 → 相同输出”更快,不是“相同输入 → 相同副作用”
真正容易被忽略的点是:缓存键完全由参数值决定,而参数值是否“相等”取决于 Python 的 == 和 hash() 行为。比如两个内容相同的 numpy.ndarray 默认不可哈希,也无法被缓存——这种时候得自己封装一层 key 构造逻辑,而不是硬套 lru_cache。


















