Python 装饰器进阶:带参数的装饰器与 functools.wraps 到底怎么写
Python 装饰器进阶:带参数的装饰器与 functools.wraps 到底怎么写写装饰器的人多,但真正把「带参数的装饰器」和「元信息丢失」这两个坑填平的人少。你大概率遇到过这两件事:一是想给装饰器传个配置(比如重试几次、限流多少),结果三层嵌套的函数绕晕了;二是被装饰后的函数__name__变成了wrapper,日志和文档全乱套,Flask 路由甚至直接报重名。这篇把这两个问题一次讲透,给出能直接抄进项目的写法。先从最朴素的装饰器说起一个装饰器本质就是「接收一个函数、返回一个函数」:deflog_call(func):defwrapper(*args,**kwargs):print(f调用{func.__name__})returnfunc(*args,**kwargs)returnwrapperlog_calldefadd(a,b):returnabprint(add(1,2))# 先打印 调用 add,再返回 3log_call等价于add log_call(add)。到这里没问题,但两个隐患已经埋下了。坑一:被装饰后,函数的身份丢了接着上面的代码看一眼函数的元信息:print(add.__name__)# wrapper —— 不是 add 了!print(add.__doc__)# None —— 原来的文档字符串没了add现在指向的是wrapper,所以名字、文档、签名全变成了wrapper的。这在小脚本里无所谓,但一旦上规模就会出事:日志里全打印成wrapper,根本分不清是哪个函数在跑;用functools.lru_cache、pytest参数化这类依赖函数名的工具会错乱;Web 框架(Flask、FastAPI)常用函数名做路由端点名,多个函数都叫wrapper会直接抛「端点重复」异常。修复只要一行 —— 用functools.wraps把原函数的元信息复制到wrapper上:importfunctoolsdeflog_call(func):functools.wraps(func)# 关键:把 func 的 __name__/__doc__/__wrapped__ 拷给 wrapperdefwrapper(*args,**kwargs):print(f调用{func.__name__})returnfunc(*args,**kwargs)returnwrapperlog_calldefadd(a,b):两数相加returnabprint(add.__name__)# addprint(add.__doc__)# 两数相加functools.wraps本身也是个装饰器,它会把func的__name__、__doc__、__module__、__dict__拷过来,还会设置__wrapped__指回原函数,方便你在需要时拿到未包装的版本。写装饰器时无脑加上它,这是行业惯例。坑二:我想给装饰器传参数怎么办真实需求几乎都带配置:重试 3 次、限流每秒 5 次、日志级别设成 WARNING。但装饰器只接收一个「函数」参数,配置往哪塞?答案是再套一层 ——写一个「返回装饰器的函数」。也就是说,retry(times3)里的retry(times3)先执行,返回一个真正的装饰器,再去装饰函数:importfunctoolsimporttimedefretry(times3,delay0.5,exceptions(Exception,)):失败自动重试。times 次都失败才抛出最后一次异常。defdecorator(func):functools.wraps(func)defwrapper(*args,**kwargs):last_excNoneforattemptinrange(1,times1):try:returnfunc(*args,**kwargs)exceptexceptionsase:last_exce# 最后一次就不用再睡了,直接跳出去抛异常ifattempttimes:print(f[{func.__name__}] 第{attempt}次失败:{e},{delay}s 后重试)time.sleep(delay)raiselast_excreturnwrapperreturndecoratorretry(times3,delay1,exceptions(ConnectionError,))deffetch(url):raiseConnectionError(连接超时)fetch(http://example.com)理清这三层的执行顺序,整件事就通了:retry(times3, delay1, ...)先被调用,返回内层的decorator;decorator(fetch)再被调用,返回wrapper;最终fetch指向wrapper。记忆点:带参数的装饰器 三层函数,最外层收「装饰器参数」,中间层收「被装饰的函数」,最里层收「调用时的实参」。进阶:让装饰器「带不带参数都能用」上面的retry必须写成retry(),忘了括号写成retry就会报错,因为retry会把func当成times收走。想做到retry和retry(times5)都能用,可以判断第一个位置参数是不是可调用对象:importfunctoolsdefretry(_funcNone,*,times3,delay0.5):defdecorator(func):functools.wraps(func)defwrapper(*args,**kwargs):forattemptinrange(1,times1):try:returnfunc(*args,**kwargs)exceptException:ifattempttimes:raisereturnwrapper# retry 直接用:_func 是被装饰的函数,立刻装饰返回if_funcisnotNone:returndecorator(_func)# retry(times5) 用法:_func 是 None,返回装饰器等下一步returndecoratorretry# 没括号,走 _func 分支defa():...retry(times5)# 有括号,走 return decorator 分支defb():...*强制times、delay只能用关键字传,避免有人写retry(5)时把 5 误当成_func。这个模式在标准库里很常见,dataclasses.dataclass就是这么实现的。用 class 写装饰器(需要保存状态时更清晰)如果装饰器要累积状态(比如统计调用次数),用类比闭包更直观。此时用functools.update_wrapper代替wraps:importfunctoolsclassCountCalls:def__init__(self,func):functools.update_wrapper(self,func)# 等价于 wraps,拷贝元信息到实例self.funcfunc self.count0def__call__(self,*args,**kwargs):self.count1print(f{self.func.__name__}第{self.count}次调用)returnself.func(*args,**kwargs)CountCallsdefhello():print(hi)hello();hello()print(hello.count)# 2 —— 状态存在实例上,读取很自然wraps是给函数用的语法糖,update_wrapper是它底层调用的函数,类装饰器里手动调一下即可。小结写任何装饰器都加functools.wraps(func),否则被装饰函数的__name__/__doc__会丢,日志、缓存、Web 路由都可能出问题。带参数的装饰器 三层嵌套:最外层收装饰器参数,中间层收函数,最里层收调用实参;记住retry(times3)是「先调用retry拿到装饰器」。想做到「带不带括号都能用」,判断第一个参数是否可调用,并用*把配置项强制成关键字参数。需要保存状态时用类装饰器 functools.update_wrapper,比闭包更好读。一句话记忆:装饰器传参就多包一层函数,元信息丢失就functools.wraps补回来。

相关新闻

Docker 存储与日志 3 大资源耗尽场景实战:overlay2 驱动优化与日志轮转配置

Docker 存储与日志 3 大资源耗尽场景实战:overlay2 驱动优化与日志轮转配置

Docker 存储与日志资源耗尽场景深度优化指南引言:当Docker开始"吃"你的磁盘空间时凌晨三点,刺耳的告警铃声划破夜空——生产环境Docker主机再次触发"no space left on device"警报。这不是第一次,也不会是最后一次。对于…

2026/7/9 18:03:46阅读更多 →
2026最新小红书去水印方法,官方无水印保存免费教程

2026最新小红书去水印方法,官方无水印保存免费教程

日常使用小红书收藏优质图文、视频素材时,自带的平台水印总会影响画面完整性,不管是个人学习参考、素材整理存档,都需要干净无水印的原版内容。2026年全网各类去水印工具层出不穷,不少工具存在广告弹窗、隐私泄露、付费套路、解析…

2026/7/9 18:03:46阅读更多 →
AI驱动WordPress建站:零代码打造一人公司官网,5分钟快速上线

AI驱动WordPress建站:零代码打造一人公司官网,5分钟快速上线

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 “一个人就能撑起一家公司”,这句话在AI时代正从口号变成现实。尤其在网站建设领域,过去需要设计师、前端、后…

2026/7/9 18:03:46阅读更多 →
Cocos Creator热更新实战:从原理到避坑的完整解决方案

Cocos Creator热更新实战:从原理到避坑的完整解决方案

1. 项目概述:为什么热更新是Cocos项目开发的“刚需”?做Cocos Creator项目,尤其是手游,最怕什么?怕的不是代码写不出来,而是版本发出去之后,发现一个致命Bug,或者想紧急加个活动。这…

2026/7/9 21:29:37阅读更多 →
从《愚者之夜》到实战:Blender与UE5创建混合生物数字角色全流程

从《愚者之夜》到实战:Blender与UE5创建混合生物数字角色全流程

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 如果你最近在关注AI动画、数字人或者3D建模领域,可能会发现一个趋势:技术正在以前所未有的速度,将…

2026/7/9 21:29:37阅读更多 →
iOS个人开发者APP备案:无服务器、纯本地应用3个解决方案实测

iOS个人开发者APP备案:无服务器、纯本地应用3个解决方案实测

iOS个人开发者APP备案实战指南:无服务器应用的3种合规路径去年夏天在App Store提交一款离线笔记工具时,突然收到苹果要求补充ICP备案号的邮件。作为纯本地应用开发者,我翻遍工信部官网却发现个人根本无法直接备案——这个看似简单的流程背后藏…

2026/7/9 21:29:37阅读更多 →
基于STM32单片机智能厨房安全控制天然气甲烷检测火焰火灾报警321(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_

基于STM32单片机智能厨房安全控制天然气甲烷检测火焰火灾报警321(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_

基于STM32单片机智能厨房安全控制天然气甲烷检测火焰火灾报警321(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_ 版本1:火焰水泵声光报警自动/手动模式火焰传感器检测当前厨房是否发生明火按键一:自动模式 按键二&a…

2026/7/9 21:29:37阅读更多 →
Unity DOTS多人游戏47ms延迟突增:Netcode指令调度与Ghost同步优化实战

Unity DOTS多人游戏47ms延迟突增:Netcode指令调度与Ghost同步优化实战

1. 项目概述:当DOTS 2.0遇上Netcode for Entities 最近在折腾一个基于Unity DOTS 2.0架构的多人对战项目,核心网络同步方案用的是Netcode for Entities。项目上线前进行跨服压力测试时,我们遇到了一个非常棘手的问题:在特定场景下…

2026/7/9 21:29:37阅读更多 →
for循环和while循环——我终于让程序能重复做一件事了

for循环和while循环——我终于让程序能重复做一件事了

一、while循环&#xff1a;不知道要重复多少次的时候 while循环的意思是&#xff1a;当条件成立时&#xff0c;一直重复执行某段代码。 先看一个最简单的例子&#xff1a; count 0 while count < 5: print(f"第{count 1}次循环") count count 1 运行结果&…

2026/7/9 21:24:37阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述&#xff1a;从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目&#xff0c;叫 skills4/skills &#xff0c;它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景&#xff1a;一个旨在展示或教授某种技能的仓库&#xff0c;本身却成了安…

2026/7/9 5:56:19阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示&#xff1a;因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战&#xff1a;从“黑箱预测”到“可信推理”2026年6月&#xff0c;第7届机器学习与趋势国际会议&#xff08;MLT 2026&#xff09;将在悉尼召开。会议议程中&#xff0c;“因果与可解释机器学习…

2026/7/9 9:45:20阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时&#xff0c;通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中&#xff0c;是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/9 2:47:07阅读更多 →
Three.js 着色器光效教程

Three.js 着色器光效教程

着色器光效 Shader Light ▶ 在线运行案例 案例合集&#xff1a; 三维可视化功能案例&#xff08;threehub.cn&#xff09;开源仓库github地址&#xff1a; https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial 自…

2026/7/9 0:04:37阅读更多 →
如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理&#xff1a;开源工具CASEMOVE终极指南 【免费下载链接】casemove A dedicated desktop app that enables you to move items in and out of storage units in CS2. 项目地址: https://gitcode.com/gh_mirrors/ca/casemove 还在为CS2存储单…

2026/7/9 0:04:37阅读更多 →
GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a与MySQL 8.0&#xff1a;ALTER TABLE语法差异深度解析与实战指南1. 两种数据库的ALTER TABLE能力全景对比在数据库架构设计和运维过程中&#xff0c;表结构变更&#xff08;DDL操作&#xff09;是不可避免的需求。GBase 8a作为国产分析型数据库代表&#xff0c;与开源M…

2026/7/9 0:04:37阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时&#xff0c;发现推理速度只有可怜的 1-2 FPS&#xff0c;而别人的演示视频却能跑到 30 FPS 以上&#xff0c;那么问题很可能不在模型本身&#xff0c;而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后&#xff0c;会直接使用官方示例…

2026/7/9 9:45:20阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一&#xff1a;为什么你需要了解 Coze 和 Dify&#xff1f;如果你对 AI 应用开发感兴趣&#xff0c;但一看到“大模型”、“智能体”、“工作流”这些词就头疼&#xff0c;觉得门槛太高&#xff0c;那这篇文章就是为你准备的。很多开发者&#xff0c;包括我自己&#…

2026/7/9 15:50:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会&#xff1a;配图一直是个让人头疼的问题。2026年&#xff0c;AI生图工具已经非常成熟了&#xff0c;但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1&#xff1a;速度之王2026年6月11日&#xff0c…

2026/7/9 14:14:17阅读更多 →