lru_cache装饰器通过maxsize参数控制缓存容量:None为无限制,正整数指定最多缓存的参数组合数,0则禁用缓存;参数须可哈希,支持cache_info()查看状态、cache_clear()清空缓存。

lru_cache 装饰器怎么用,参数 maxsize 控制什么
functools.lru_cache 是 Python 内置的轻量级 LRU 缓存实现,直接装饰函数即可启用缓存。核心参数是 maxsize:设为 None 表示无容量限制(但实际受内存约束),设为正整数(如 128)表示最多缓存多少个不同参数组合的结果;设为 0 则**完全禁用缓存**(仅用于调试或临时关闭)。
注意:maxsize 统计的是「调用时传入的参数组合」的唯一性,不是返回值数量。例如 fib(5) 和 fib(6) 算两个独立缓存项。
常见误用:@lru_cache(maxsize=1) 并不等于“只记住最后一次结果”——它只是最多存 1 个键值对,但键由全部参数决定,和调用顺序无关。
被装饰函数的参数必须是可哈希的
lru_cache 底层用字典存缓存,所以所有位置参数和关键字参数都必须是可哈希类型(str、int、tuple 等)。一旦传入 list、dict、set 或自定义不可哈希对象,会立即抛出 TypeError: unhashable type。
立即学习“Python免费学习笔记(深入)”;
典型场景:
- 想缓存数据库查询结果,但把
where_conditions=dict(status='active')当作参数传入 → 报错 - 递归函数传了
cache={}这种默认可变参数 → 不仅报错,还会污染缓存逻辑
解决办法:手动把不可哈希参数转成可哈希形式,比如用 tuple(sorted(kwargs.items())) 或 json.dumps(kwargs, sort_keys=True)(需确保内容可序列化)。
缓存命中率低?检查是否无意中创建了新对象
即使参数看起来一样,如果每次调用都新建对象(尤其是 tuple、frozenset),也可能导致缓存失效。例如:
@lru_cache(maxsize=128)
def process(items):
return sum(items)
<h1>下面两次调用不会命中缓存:</h1><p>process((1, 2, 3)) # tuple 对象 A
process((1, 2, 3)) # tuple 对象 B —— 即使值相同,Python 中 tuple 是可哈希且相等的,这里其实能命中
真正危险的是这种:
process([1, 2, 3]) # TypeError,根本走不到缓存 process(tuple([1, 2, 3])) # OK,但若每次调用都 new 一个 tuple,只要值相同,仍能命中(tuple 是不可变且 __eq__ 正常)
更隐蔽的问题:使用 dataclass 或 namedtuple 时没设 frozen=True,导致实例不可哈希;或者用了带 __slots__ 但没实现 __hash__ 的类。
如何查看/清空缓存状态
每个被 @lru_cache 装饰的函数都会多出两个方法:cache_info() 和 cache_clear()。
cache_info() 返回命名元组,含四个字段:
-
hits:缓存命中次数 -
misses:未命中次数(触发实际计算) -
maxsize:当前设定上限 -
currsize:当前已缓存条目数
调试时建议在关键路径后加一句 print(my_func.cache_info()),比猜更可靠。需要重置缓存(比如单元测试里)就调用 my_func.cache_clear() —— 注意它不接受参数,也不能清空部分键。
容易忽略的一点:cache_clear() 对所有该函数的调用共享生效,包括不同线程中的调用(因为缓存是函数对象的属性),但 lru_cache 本身不保证线程安全——高并发下 hits/misses 计数可能不准,不过缓存逻辑不会崩溃。


















