递归函数慢是因为重复计算子问题,如fib(40)中fib(38)被多次调用;加@lru_cache()可复用结果提升性能,但需确保参数可哈希、合理设置maxsize并注意线程安全。

为什么递归函数慢得明显?先看没缓存时的典型表现
递归本身不慢,慢在重复计算。比如 fib(40) 调用 fib(39) 和 fib(38),而 fib(39) 又会再次调用 fib(38)、fib(37)……整棵树里同一子问题被算几十上百次。
不加缓存的 fib 函数跑 fib(35) 就要几百毫秒;换成 @lru_cache() 后,同一输入首次计算后结果直接复用,后续调用几乎零开销。
常见错误现象包括:
- 函数返回值看似正确,但执行时间随输入线性增长 → 实际是缓存没生效(比如参数含不可哈希类型)
- 内存占用飙升 →
maxsize设得太大或没设限,缓存了太多历史调用 - 多线程下结果错乱 →
lru_cache不是线程安全的,但单线程场景完全没问题
怎么加 @lru_cache 才真正生效?注意三个硬约束
lru_cache 要求所有参数都可哈希(hashable),否则运行时报 TypeError: unhashable type。
必须满足:
- 参数不能是
dict、list、set这类可变类型 - 如果要用字典传参,得转成
tuple(sorted(d.items()))或用types.MappingProxyType包一层 - 自定义类实例需实现
__hash__且确保逻辑与__eq__一致
示例:下面这段会报错
@lru_cache()
def calc(data_list):
return sum(data_list)
calc([1, 2, 3]) # TypeError: unhashable type: 'list'
maxsize 参数怎么选?别盲目设 None
maxsize 控制缓存条目上限,默认是 128;设为 None 表示无限制,但容易吃光内存,尤其参数组合多、返回值大时。
建议策略:
- 纯数值递归(如斐波那契、阶乘)→
maxsize=None安全,状态空间小 - 带字符串参数的解析函数(如
parse_expr("a + b * c"))→ 先测典型输入量,设为1024或4096更稳妥 - 调试阶段加
print(cache_info())看命中率:cache_info().hits / (cache_info().hits + cache_info().misses)低于 0.7 就说明缓存利用率低,可能参数太发散或 maxsize 太小
递归深度大时,lru_cache 会不会拖慢首次调用?
不会拖慢,但会略微增加首次调用的开销——主要是构建缓存哈希表和包装函数对象,通常在纳秒级。真正影响性能的是缓存键的哈希计算本身。
关键点:
- 参数是简单类型(
int、str、tuple)时,哈希极快;嵌套深的tuple或长str才值得留意 - 如果函数本身耗时以毫秒计,这点开销可忽略;但如果单次执行本就在微秒级,又高频调用,建议用
functools.cache(Python 3.9+,比lru_cache(maxsize=None)略轻量) - 缓存失效只发生在新参数首次出现时,不是每次递归都查表——内部是树形调用链中逐层复用,效率很高
实际项目里最容易被忽略的是参数可哈希性检查,尤其是从 JSON 加载数据后直接传给缓存函数,里面混着 list 却没意识到。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











