lru_cache 装饰器的缓存清除机制核心围绕 LRU(Least Recently Used,最近最少使用)算法 展开,仅在特定条件下触发清除,且分为「被动淘汰」「主动清除」「隐式清除」三类场景。以下是详细拆解:
一、核心:LRU 淘汰算法(被动清除的核心逻辑)
lru_cache 的核心目标是:当缓存条目数量达到 maxsize 上限时,自动淘汰「最近最少被访问」的缓存条目,从而控制内存占用。
LRU 算法的工作原理
- 缓存的存储结构:
lru_cache内部通过「哈希表 + 双向链表」(Python 3.3+ 后优化为更高效的_OrderedDict/_LRUCache结构)维护缓存:- 哈希表:快速映射「函数参数→返回值」,保证 O(1) 时间复杂度的查询;
- 双向链表:记录缓存条目的「访问时间顺序」,链表头部是「最近使用(MRU)」的条目,尾部是「最近最少使用(LRU)」的条目。
- 访问更新规则:
- 当函数调用命中缓存(参数已存在):该条目会被移到链表头部(标记为“最近使用”);
- 当函数调用未命中缓存(参数不存在):新条目会被添加到链表头部。
- 淘汰触发规则:
当新条目添加后,缓存总数超过maxsize上限时,直接删除链表尾部的条目(即“最近最少使用”的缓存)。
二、触发缓存清除的三类场景
1. 被动清除:达到 maxsize 上限时的 LRU 淘汰(核心场景)
这是 lru_cache 最核心的自动清除机制,仅在 maxsize 为正整数时生效。
示例:直观展示 LRU 淘汰过程
from functools import lru_cache# 设置缓存上限为 2(maxsize=2)
@lru_cache(maxsize=2)
def func(x):print(f"执行函数:x={x}")return x# 第一次调用:x=1,未命中,添加到缓存(缓存:{1:1})
func(1) # 输出:执行函数:x=1
# 第二次调用:x=2,未命中,添加到缓存(缓存:{1:1, 2:2},已达maxsize)
func(2) # 输出:执行函数:x=2
# 第三次调用:x=1,命中,将1移到“最近使用”端(缓存顺序:[1,2])
func(1) # 无输出(命中缓存)
# 第四次调用:x=3,未命中,添加后缓存超上限(3条),淘汰尾部的2(最近最少使用)
func(3) # 输出:执行函数:x=3(缓存变为:{1:1, 3:3})
# 第五次调用:x=2,未命中(已被淘汰),需重新执行
func(2) # 输出:执行函数:x=2(缓存变为:{3:3, 2:2},淘汰1)# 查看缓存信息:hits=1(x=1命中),misses=4(x=1/2/3/2未命中),currsize=2
print(func.cache_info())
2. 主动清除:手动调用 cache_clear() 清空所有缓存
lru_cache 为装饰后的函数提供了 cache_clear() 方法,可强制清空所有缓存条目(无论是否达到 maxsize),是最常用的手动清除方式。
示例:主动清空缓存
from functools import lru_cache@lru_cache(maxsize=5)
def add(a, b):return a + b# 调用函数,填充缓存
add(1, 2)
add(3, 4)
print(add.cache_info()) # CacheInfo(hits=0, misses=2, maxsize=5, currsize=2)# 手动清空缓存
add.cache_clear()
print(add.cache_info()) # CacheInfo(hits=0, misses=0, maxsize=5, currsize=0)
3. 隐式清除:进程/程序结束(缓存丢失)
lru_cache 的缓存是进程内的内存缓存,存储在 Python 解释器的内存中:
- 当程序退出、进程终止(如重启脚本、关闭进程),缓存会被操作系统回收,全部丢失;
- 多进程场景下,每个进程的缓存相互独立,一个进程的缓存不会影响另一个进程。
三、maxsize 参数对清除机制的关键影响
maxsize 是控制 lru_cache 清除机制的核心参数,不同取值对应完全不同的行为:
| maxsize 取值 | 清除机制行为 |
|---|---|
| None | 禁用 LRU 淘汰,缓存无上限,不会自动清除(除非手动 cache_clear() 或进程结束);风险:缓存可能无限增长,导致内存泄漏。 |
| 正整数(如 128) | 启用 LRU 淘汰,缓存条目达到该数值时,自动淘汰“最近最少使用”的条目。 |
| 0 | 完全禁用缓存(相当于没加装饰器),每次调用函数都会重新执行,无缓存存储。 |
四、补充:易混淆的“非清除”场景
以下情况并非缓存清除,而是缓存未命中,需注意区分:
- 参数类型/值变化:如
func(1)(int)和func(1.0)(float),当typed=False时视为同一参数(命中),typed=True时视为不同参数(未命中,新增条目); - 不可哈希参数:列表、字典等不可哈希参数无法被缓存,每次调用都会执行函数(并非清除,而是从未存储);
- 函数逻辑修改:修改函数代码后,缓存不会自动更新(缓存的是旧代码的返回值),需手动
cache_clear()。
五、总结
lru_cache 的缓存清除机制可归纳为:
- 自动清除:仅在
maxsize为正整数且缓存满时,按 LRU 算法淘汰“最近最少使用”的条目; - 手动清除:调用
cache_clear()清空所有缓存; - 隐式清除:程序/进程结束后,内存缓存被回收;
- 无「过期自动清除」机制(如需过期,需用
cachetools.TTLCache等扩展库)。
理解这一机制后,可通过合理设置 maxsize(如根据参数数量设置)、结合 cache_clear() 手动清理,避免内存占用过高,同时保证缓存命中率。