lru_cache没生效最常见原因是参数含不可哈希类型(如list、dict),导致直接跳过缓存而非命中失败;maxsize应据调用分布调整,过小致频繁淘汰,过大引内存风险;默认参数须避免可变对象,宜用none初始化;多线程下虽线程安全但有锁开销。

为什么 lru_cache 有时没生效?
缓存失效最常见原因是函数参数含不可哈希类型,比如 list、dict、set。一旦参数里出现这些,lru_cache 直接抛出 TypeError: unhashable type,根本不会缓存——不是“没生效”,而是根本启动不了。
实操建议:
- 检查被装饰函数的所有参数类型,确保全是不可变类型(
str、int、tuple、FrozenSet等) - 若必须传
dict,先转成tuple(sorted(d.items()));传list可转tuple(my_list) - 避免在参数中混用
None和缺失值逻辑——None是合法哈希值,但容易和业务逻辑冲突
lru_cache(maxsize=128) 的 maxsize 设多少才合适?
默认 maxsize=128 是经验值,但实际应按调用频次和参数组合数判断。设太小导致频繁淘汰,设太大则内存占用不可控,尤其当参数本身是大对象(如长字符串、嵌套元组)时。
实操建议:
- 先用
lru_cache(maxsize=None)测试,配合my_func.cache_info()观察hits/misses比例和currsize - 若
currsize稳定在 20 左右,maxsize=32就够用;若涨到 500+,需警惕参数爆炸(如多层嵌套或时间戳粒度太细) - 明确业务场景:查配置用
maxsize=1即可;递归斐波那契用maxsize=128足够;高频路由匹配可能需要maxsize=1024
带默认参数的函数被 lru_cache 缓存时要注意什么?
Python 函数的默认参数在定义时求值,而 lru_cache 的 key 是基于调用时的实际参数生成的。这意味着 def f(x, y=[]) 这种写法,即使你没传 y,每次调用的 y 都是同一个可变对象——缓存 key 却认为它们“相同”,导致意外复用结果。
实操建议:
- 永远用
None作默认值,内部再初始化:def f(x, y=None): y = y or [] - 如果默认值依赖运行时状态(如当前时间),不要缓存整个函数,改用带时间窗口的局部缓存或手动控制
- 检查
cache_info()中的misses是否异常高——可能是默认参数引发的 key 冲突
多线程下 lru_cache 安全吗?
lru_cache 是线程安全的,内部用了 threading.RLock,但性能代价明显:每次缓存访问都带锁开销。高并发场景下,如果函数本身很快(比如纯计算
实操建议:
- 用
timeit对比加缓存前后在多线程下的吞吐量,别只看单线程加速比 - 若瓶颈在锁竞争,考虑降级为
functools.cache(Python 3.9+,无 maxsize 限制但仍是线程安全)或改用concurrent.futures.ThreadPoolExecutor+ 手动 dict 缓存(需自行加锁) - 避免在缓存函数里做 I/O 或长时间阻塞操作——这会让锁持有太久,拖慢其他线程
缓存不是银弹,关键是确认重复调用是否真存在、参数是否稳定、以及缓存生命周期是否匹配业务节奏。盲目加 @lru_cache 可能引入隐蔽的内存泄漏或状态错乱。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











