Python @lru_cache LRU缓存装饰器

介绍

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)  # 走缓存

不用场景

  1. 函数参数含 list/dict/set 且不方便转元组
  2. 函数结果实时变化(接口、传感器、动态数据)
  3. 调用频次极低,缓存收益小于内存开销
  4. 需要分布式缓存(多机器共享)→ 改用 Redis 等

优缺点

优点

  1. 一行装饰器,开箱即用,零开发成本
  2. 自动 LRU 淘汰,不用手动管理缓存
  3. 大幅降低重复计算、IO、查询耗时

缺点 & 注意事项

  1. 仅支持可哈希参数,列表、字典等无法直接使用
  2. 占用内存:缓存结果常驻内存,大量数据慎用
  3. 不适合结果频繁变化的非纯函数
  4. 不支持多进程共享缓存(仅当前进程/线程生效)

原文出处:/show_1GW3OtCAm92y.html