介绍
lru_cache 是 Python functools 模块提供的 装饰器,实现 最近最少使用(LRU)缓存,用来缓存函数返回结果,避免重复计算,大幅 提升 重复调用的执行效率。
基本概念
- LRU:Least Recently Used,最近最少使用。缓存满时,自动淘汰最久没被调用的结果。详见 链接
- 作用:相同入参 → 直接返回缓存结果,不再执行函数体。
- 位置:
from functools import lru_cache
基础用法
1. 最简使用
from functools import lru_cache
@lru_cache
def add(a, b):
print("执行计算")
return a + b
add(1, 2) # 执行计算 → 返回3
add(1, 2) # 直接读缓存,不打印
add(2, 3) # 新参数,重新执行
2. 设置缓存最大容量
maxsize 参数:限制缓存条目数量
- 不传值:缓存数量最多 128,达到 128 条后,触发 LRU 淘汰规则,移除最久未使用的数据
maxsize=数字:最多缓存 N 条,满了自动 LRU 淘汰maxsize=None(默认):无上限缓存、关闭淘汰
@lru_cache(maxsize=3) # 最多存3条结果
def func(x):
return x * 2
3. typed 参数(区分参数类型)
typed=False(默认):不区分类型,1和1.0视为同一个参数typed=True:区分类型,int(1)和float(1.0)算不同入参
@lru_cache(typed=True)
def show(x):
return type(x)
show(1)
show(1.0) # 重新执行,不会命中缓存
限制
1. 函数参数必须是可哈希类型
可哈希:int/float/str/tuple/frozenset/None
不可哈希不能缓存:list/dict/set
❌ 错误示例:
@lru_cache
def f(lst):
return sum(lst)
f([1,2,3]) # 报错:TypeError: unhashable type: 'list'
✅ 解决方案:把列表/字典转为元组
@lru_cache
def f(t):
return sum(t)
f((1,2,3))
2. 不支持可变参数、全局变量、外部状态
如果函数结果依赖全局变量/外部可变对象,缓存会出错:
from functools import lru_cache
num = 10
@lru_cache
def get_num(x):
return x + num
get_num(1) # 11
num = 20
get_num(1) # 依旧返回 11(读旧缓存,不会感知全局变量变化)
结论:被装饰函数必须是纯函数(相同入参永远得到相同返回)。
常用内置方法
1. 查看缓存信息
cache_info():返回命名组 hits(命中数), misses(未命中), maxsize(最大容量), currsize(当前数量)
print(add.cache_info())
# CacheInfo(hits=1, misses=2, maxsize=None, currsize=2)
2. 清空所有缓存
cache_clear():手动清空缓存,重新开始计数
add.cache_clear()
使用场景
场景1:递归(斐波那契数列,最经典)
无缓存时递归重复计算,时间复杂度爆炸;加 lru_cache 直接优化到 O(n)
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
print(fib(30))
场景2:重复查询/IO 操作
网络请求、文件读取、数据库查询等慢操作,缓存结果减少开销:
@lru_cache(maxsize=128)
def query_data(uid):
# 模拟数据库查询
print("查询数据库")
return f"用户{uid}数据"
query_data(1001)
query_data(1001) # 走缓存
不用场景
- 函数参数含
list/dict/set且不方便转元组 - 函数结果实时变化(接口、传感器、动态数据)
- 调用频次极低,缓存收益小于内存开销
- 需要分布式缓存(多机器共享)→ 改用 Redis 等
优缺点
优点
- 一行装饰器,开箱即用,零开发成本
- 自动 LRU 淘汰,不用手动管理缓存
- 大幅降低重复计算、IO、查询耗时
缺点 & 注意事项
- 仅支持可哈希参数,列表、字典等无法直接使用
- 占用内存:缓存结果常驻内存,大量数据慎用
- 不适合结果频繁变化的非纯函数
- 不支持多进程共享缓存(仅当前进程/线程生效)