Python装饰器从入门到生产实践:闭包、functools.wraps 与三层嵌套一次讲透

举报
yd_252044305 发表于 2026/11/05 09:56:14 2026/11/05
【摘要】 Python 的装饰器是面试高频题,也是很多工程师"会用但说不清"的知识点。今天用一篇短文把装饰器从闭包讲到生产实践,帮你把这个知识点彻底吃透。一、装饰器的本质:闭包 + 语法糖装饰器就是一个"接收函数、返回新函数"的调用。@decorator 只是 func = decorator(func) 的语法糖。理解它的前提是理解闭包:内层函数引用了外层作用域的变量,并且这个引用在函数返回后依然存...

Python 的装饰器是面试高频题,也是很多工程师"会用但说不清"的知识点。今天用一篇短文把装饰器从闭包讲到生产实践,帮你把这个知识点彻底吃透。


一、装饰器的本质:闭包 + 语法糖


装饰器就是一个"接收函数、返回新函数"的调用。@decorator 只是 func = decorator(func) 的语法糖。理解它的前提是理解闭包:内层函数引用了外层作用域的变量,并且这个引用在函数返回后依然存活。


最简示例:


def log_call(func):

    def wrapper(*args, **kwargs):

        print(f"调用 {func.__name__}")

        return func(*args, **kwargs)

    return wrapper


@log_call

def query_user(uid):

    ...


调用 query_user(1) 时实际执行的是 wrapper(1),它先打印日志再调用原函数。


二、三个必踩的坑


1. 丢失元信息。wrapper 覆盖了原函数的 __name__、__doc__,调试和文档生成全乱。解法:给 wrapper 加上 functools.wraps(func),一行解决。


2. 装饰带返回值的函数忘记 return。wrapper 里只写 func(*args, **kwargs) 而不 return,调用方拿到的永远是 None。这类 bug 在重构时经常出现,单测能兜住。


3. 装饰器只在导入时执行一次。想用装饰器做"每次调用都生效"的逻辑,必须写在 wrapper 内部;写在装饰器函数体里的代码只在被装饰那一刻跑一遍。


三、带参数的装饰器:三层嵌套


@retry(times=3, delay=1)

def fetch(url): ...


带参数的装饰器是三层结构:最外层接收参数,中层接收函数,内层是 wrapper。写的时候记住"装饰器工厂"这个叫法就不容易乱:retry(times=3) 返回真正的装饰器,再由它包装 fetch。


四、生产中的高频应用


1. 重试与熔断:对网络请求包一层指数退避重试,注意只对幂等操作重试,POST 下单这类接口重试可能造成重复扣款。


2. 缓存:functools.lru_cache 是现成的装饰器,适合纯函数;带过期时间的缓存需要自己写,注意缓存 key 要把参数序列化进去。


3. 权限与审计:Web 框架的路由守卫本质就是装饰器,Flask 的 @app.route、鉴权切面都是同一套路。


4. 性能埋点:用 time.perf_counter 包住函数计时,比在业务代码里插 print 干净得多。


五、一条心法


装饰器适合做"与业务无关的横切逻辑":日志、重试、缓存、鉴权。一旦装饰器里开始出现业务 if 判断,就该停下来想想是不是抽象错了。保持装饰器无业务语义,它才能在整个项目里复用。


把闭包、functools.wraps、三层嵌套这三件事讲清楚,装饰器就不神秘了。下次面试被问到,不妨从"它只是函数调用的一种写法"开始讲起。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。