发布时间:2026/9/6 8:51:04
Python functools.lru_cache 实战:一行加缓存、可变参数坑与手动失效 Python functools.lru_cache 实战:一行加缓存、可变参数坑与手动失效有个函数算得慢,或者要反复调远程接口,你想「同样的入参别重复算」。手写一个 dict 当缓存?几行就能踩三个坑:线程安全、缓存无上限撑爆内存、失效逻辑一堆。其实标准库的functools.lru_cache一行装饰器就搞定了大半——但它也有几个不看文档必踩的坑。这篇讲清楚怎么用、什么时候会翻车、怎么手动控制。从手写缓存的痛点说起先看没缓存的递归斐波那契,指数级重复计算:deffib(n):ifn2:returnnreturnfib(n-1)fib(n-2)# fib(35) 要算好几秒,因为同一个 fib(k) 被重复算了成千上万次手写缓存版:_cache{}deffib(n):ifnin_cache:return_cache[n]resultnifn2elsefib(n-1)fib(n-2)_cache[n]resultreturnresult能用,但问题一堆:_cache是个全局变量污染命名空间、无上限会一直涨、多线程下in判断和写入之间有竞态、想清空还得手动_cache.clear()。这些lru_cache都替你处理好了。一行搞定fromfunctoolsimportlru_cachelru_cache(maxsizeNone)# None 表示不限容量deffib(n):ifn2:returnnreturnfib(n-1)fib(n-2)print(fib(100))# 瞬间出结果,每个 fib(k) 只算一次lru_cache帮你做了:自动用参数当 key 缓存返回值、LRU(最近最少使用)淘汰、线程安全的读写。名字里的 LRU 指的是——当缓存条数超过maxsize,自动淘汰最久没被访问的那条。maxsize怎么选:参数取值有限、想全缓存(如斐波那契、配置解析):maxsizeNone。参数空间大、怕内存涨:给个上限如maxsize1024,超了自动淘汰旧的。Python 3.9 如果就是想「无限缓存」,直接用cache更语义化(等价于lru_cache(maxsizeNone))。坑一:参数必须可哈希,list/dict 直接报错lru_cache拿参数当字典的 key,所以参数必须可哈希(hashable)。传 list、dict、set 会当场炸:lru_cachedefprocess(items):returnsum(items)process([1,2,3])# TypeError: unhashable type: list解法是把可变参数换成不可变的:lru_cachedefprocess(items):# items 现在期望是 tuplereturnsum(items)process((1,2,3))# 传 tuple,OK如果调用方手上是 list,在调用前转一下:process(tuple(my_list))。要缓存「基于 dict 配置」的函数,可以把 dict 转成排序后的tuple(sorted(d.items()))再传。坑二:关键字参数和位置参数算不同的 keylru_cache区分「参数是位置传的还是关键字传的」。同样的逻辑入参,写法不同会被当成两次不同调用,各缓存一份:lru_cachedefadd(a,b):print(f计算{a}{b})returnab add(1,2)# 打印计算 12,算一次add(1,2)# 命中缓存,不打印add(a1,b2)# 又打印计算 12!因为 key 和位置传参不同后果是缓存命中率下降、缓存里存了重复内容。实战建议:对要缓存的函数,团队约定统一调用风格(要么都位置传、要么都关键字传),或者在函数签名里用/把参数限制为仅位置参数,从源头消除歧义。坑三:别缓存「有副作用」或「结果会变」的函数lru_cache的前提是纯函数——同样的入参永远返回同样的结果,且没有副作用。违背这个前提就会出诡异 bug:lru_cachedefget_user_config(user_id):# 危险:数据库里的配置会变,但缓存永远返回第一次读到的值returndb.query(SELECT * FROM config WHERE user_id?,user_id)用户在数据库里改了配置,你的函数还在返回旧值,而且你可能查半天都找不到原因。规则:结果会随时间/外部状态变化的函数,不要无脑套 lru_cache;要缓存也得配一个明确的失效策略。同理,别缓存返回可变对象的函数——调用方拿到缓存的 list 后改了它,会污染缓存里的那份:lru_cachedefget_defaults():return[a,b]# 返回的是同一个 list 对象dget_defaults()d.append(c)# 改的是缓存里那个 list!print(get_defaults())# [a, b, c] —— 被污染了要么返回不可变的 tuple,要么在调用处 copy 一份。手动查缓存状态与失效lru_cache装饰后的函数带两个实用方法:lru_cache(maxsize128)defslow(n):returnn*n slow(2);slow(3);slow(2)# 查命中情况:hits1 misses2 maxsize128 currsize2print(slow.cache_info())# 手动清空整个缓存(比如配置变更后)slow.cache_clear()print(slow.cache_info())# 清空后 currsize0cache_info()的hits/misses能帮你评估缓存到底有没有用——如果 hits 长期接近 0,说明这个函数根本没有重复调用,加缓存纯属浪费内存,该撤掉。cache_clear()是唯一的失效手段,但它是全量清空,没法只删某一个 key。如果你需要「按 key 精细失效」,lru_cache就不够用了,得换成手写 dict 或专门的缓存库(如cachetools,它支持 TTL 过期和单 key 删除)。一个务实的选择清单纯函数、想加缓存、参数可哈希 →cache/lru_cache(maxsizeN),一行搞定。需要过期时间(TTL)→lru_cache不支持,用cachetools.TTLCache。需要按 key 失效→lru_cache只能全清,用cachetools或自己管 dict。结果会变 / 有副作用 → 别缓存,或想清楚失效策略再缓存。小结lru_cache一行给纯函数加线程安全的 LRU 缓存,maxsizeNone(或 3.9 的cache)不限容量,给数字就自动淘汰旧条目。三个坑:参数必须可哈希(list/dict 要转 tuple)、位置传参和关键字传参算不同 key(统一调用风格)、别缓存结果会变或返回可变对象的函数。cache_info()看命中率判断缓存值不值得,cache_clear()全量失效——需要 TTL 或按 key 失效就上cachetools。记忆点:lru_cache只配得上「同样输入永远同样输出、参数可哈希」的纯函数;越界的场景,换缓存库或手写。

相关新闻

2026/9/5 12:21:23

CAD自建图块库|标准件一键入库,反复调用超省心

设计师每次绘图都要重复复制门窗、阀门、标准机械零件,反复查找图纸图块浪费大量时间,普通CAD看图工具无本地图库存储功能。浩辰 CAD看图王上线自建专属图块库,一键提取图纸内所有图块入库,支持中文命名、关键词检索、分类管理。 …

2026/9/1 18:15:58

深入解析TI Davinci VPDMA:客户端缓冲与中断机制实战指南

1. 项目概述与核心价值在嵌入式高清视频处理系统的开发中,尤其是基于德州仪器(TI)Davinci或类似SoC平台时,视频管道直接内存访问(VPDMA)的设计与调试往往是决定系统性能与稳定性的关键。它不像应用层算法那…

2026/9/1 14:05:13

企业招聘管理系统:破解四大领域用人痛点

博主介绍: 所有项目都配有从入门到精通的安装教程,可二开,提供核心代码讲解,项目指导。 项目配有对应开发文档、解析等 项目都录了发布和功能操作演示视频;项目的界面和功能都可以定制,包安装运行&#xff…

2026/9/6 8:47:23

API 2.0开发实战:错误处理与CPI性能监控最佳实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/6 8:47:23

STM32智能温控风扇从入门到实战:方案、电路与代码全解析

STM32智能温控风扇这个项目,算是我接触嵌入式以来觉得最适合入门到进阶衔接的一个综合练习。它不像跑马灯那样单纯点灯,也不像平衡车那样上来就是PID调参地狱,它刚好卡在“有点难度但踮踮脚够得着”的位置上。整篇文章我会把方案选型、原理图…

2026/9/6 8:47:23

后量子密码芯片设计:从RSA到格密码的硬件迁移与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/6 8:42:23

STM32F103+FreeRTOS:芯片没反应?小心买到假芯片!

现象描述:程序烧进去,板子却跟砖头一样安静先说个我前阵子处理的真实案例。有个朋友寄来一块自家画的STM32F103C8T6最小系统板,说代码在开发板上跑得好好的,换到这块板子上就完全没反应——LED不闪、串口不打印、连个高电平都摸不…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/5 2:45:13

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/5 2:30:42

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/5 2:46:50

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…