加了@lru_cache没提速,主要因参数不可哈希或函数有副作用;maxsize需据参数空间和复用率合理设置;递归函数仅在存在大量重复子问题时才有效;须监控cache_info()命中率并及时清理缓存。

为什么加了 @lru_cache 还是没提速?
大概率是函数不满足缓存前提:参数不可哈希,或函数不是纯函数。比如传入 list、dict、自定义对象,会直接抛 TypeError: unhashable type;而函数里调用了 time.time() 或修改了全局变量,会导致缓存结果“看似命中,实则错误”。
- 检查参数类型:只接受
int、str、tuple、frozenset等可哈希类型 - 避免副作用:不能写文件、发请求、改
self属性(类方法慎用)、依赖random或当前时间 - 若必须传列表,先转成
tuple(my_list);传字典可转tuple(sorted(d.items()))
@lru_cache 的 maxsize 怎么设才不爆内存?
默认 maxsize=128 看似安全,但对参数组合多的函数(比如带浮点、长字符串、多维索引),可能迅速占满并频繁淘汰,导致命中率暴跌。设为 None 更危险——缓存无限增长,尤其递归深度大时,fibonacci(1000) 可能吃掉几百 MB。
- 先跑几轮真实参数,用
func.cache_info()查hits和currsize,确认是否真有复用 - 参数空间小(如用户 ID 枚举值 ≤ 100):设
maxsize=None或略大于上限 - 参数含浮点或不确定长度字符串:显式设小值,如
maxsize=32,再配合typed=True避免1和1.0混淆 - 临时禁用缓存调试:设
maxsize=0,不删装饰器,也不改逻辑
递归函数用 @lru_cache 为什么反而变慢?
因为每层递归调用都独立缓存,而缓存键生成和字典查找本身有开销。如果单次计算极快(比如纳秒级位运算),加缓存反而拖累;另外,若递归分支多但重复少(如树遍历中路径基本唯一),缓存利用率低,纯属白占内存。
- 只对“指数级重复子问题”的递归有效,典型如
fibonacci、levenshtein距离 - 避免装饰器嵌套在其他装饰器外层:如用了
@wraps,确保@lru_cache在最内侧,否则缓存的是包装函数,不是原逻辑 - 验证是否真加速:用
timeit对比func(35)加/不加装饰器的耗时,别只看第一次
缓存状态怎么查、怎么清?
上线后没人看 cache_info(),结果缓存全失效却以为“优化生效了”。命中率低于 20% 基本等于白加;而配置变更、数据刷新后不清缓存,会返回过期结果。
- 每次部署后或关键逻辑更新,主动调用
func.cache_clear() - 监控时打印
func.cache_info():重点关注hits / (hits + misses),持续低于 0.5 就该重新评估 -
cache_info()返回的是命名元组,字段包括hits、misses、maxsize、currsize,别试图取索引
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











