加了 @lru_cache 没变快,主因是参数不可哈希导致缓存失效;需确保所有参数为不可变类型,用 cache_info() 检查命中率,并将装饰器直接加在被递归调用的函数上。

为什么加了 @lru_cache 还没变快?
常见现象是:函数确实加了 @lru_cache,但执行时间几乎没变化,甚至更慢。根本原因通常是被缓存的参数不可哈希(比如传了 list、dict 或自定义对象),导致缓存完全失效,每次调用都走原始逻辑。
实操建议:
- 检查函数所有参数类型——必须全是不可变类型(
int、str、tuple、frozenset等);list要转成tuple,dict要转成tuple(sorted(d.items())) - 避免在装饰器里用
@lru_cache(maxsize=None)时传入大量不同参数组合,缓存会无限增长,反而拖慢 GC 和内存访问 - 用
func.cache_info()查看命中率:Hits为 0 就说明根本没进缓存
@lru_cache 在递归函数里怎么正确用?
最典型场景是斐波那契或动态规划类问题。错误写法是把装饰器加在“外层包装函数”上,而实际递归调用的是未装饰的原函数。
实操建议:
- 装饰器必须直接加在**被递归调用的那个函数本身**上,不能加在 wrapper 上
- 如果函数有默认参数(如
def fib(n, mod=10**9+7)),注意mod也参与缓存键计算——不同mod值会视为不同调用,缓存不共享 - 示例正确写法:
@lru_cache(maxsize=None) def fib(n): if n
缓存大小设多少才合适?maxsize=128 是不是太小?
maxsize 不是越大越好。它控制的是最近最少使用(LRU)缓存的条目上限,直接影响内存占用和哈希查找开销。
实操建议:
- 对参数空间有限的函数(如
n只在 0~1000 范围),设maxsize=None安全;但要注意函数不能意外接收超范围参数,否则缓存失控 - 对参数组合极多的函数(如接受两个
str参数且长度不定),优先设小值(如16或32),再根据cache_info().currsize观察实际占用 -
maxsize=128是 Python 默认值,适合多数轻量级重复调用;若你发现misses高、hits低,说明缓存太小或参数分布太散
多线程下 @lru_cache 安全吗?
不安全——@lru_cache 的缓存字典不是线程安全的。并发调用同一函数时,可能触发缓存键冲突、计数错乱,甚至 KeyError 或返回错误结果。
实操建议:
- 绝对不要在多线程环境里直接复用同一个
@lru_cache函数;每个线程应使用独立函数实例,或改用线程安全替代方案 - 简单替代:用
functools.lru_cache+threading.local封装,或换用cached_property(仅限实例方法) - 更稳妥方案:改用
asyncio下的async_lru库,或自己基于concurrent.futures.ThreadPoolExecutor+dict+Lock实现简易线程安全缓存
@lru_cache 都只是个摆设。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











