Python 装饰器:从基础到语法糖
装饰器是 Python 中一个非常实用且常用的高级特性它能够在不修改已有函数源代码和调用方式的前提下为函数扩展额外的功能。本文将带你从基础概念出发逐步理解装饰器的原理、写法、语法糖以及实际应用。1. 什么是装饰器装饰器本质上是一个闭包函数它接收一个函数作为参数并返回一个新的函数通常是内部函数。这个新函数会在执行原函数的基础上增加一些额外的功能。装饰器的核心思想不修改已有函数的源代码不修改已有函数的调用方式通过包装的方式为函数增加功能2. 从闭包到装饰器2.1 闭包回顾闭包是指在一个内部函数中引用了外部函数的变量并且外部函数返回这个内部函数。装饰器是闭包的一种特殊形式区别在于闭包外部函数接收的是普通变量装饰器外部函数接收的是一个函数对象2.2 基础装饰器写法defdecorator(func):基础装饰器示例definner():print(我是装饰器我增加了新的功能)func()# 执行原函数returninnerdefcomment():print(这是一个发表评论的功能)# 使用装饰器commentdecorator(comment)comment()输出我是装饰器我增加了新的功能 这是一个发表评论的功能3. 装饰器的工作原理3.1 执行过程分析decorator(comment)返回内部函数innercomment decorator(comment)将原函数名重新指向inner函数调用comment()实际上调用的是inner()其中包含了原函数的功能和新增功能3.2 为什么不能直接修改原函数# ❌ 错误做法直接修改源代码defcomment():print(我要增加新的功能)# 直接添加代码print(这是一个发表评论的功能)# ❌ 错误做法改变调用方式defcomment():print(这是一个发表评论的功能)defenhanced_comment():# 创建新函数print(新增功能)comment()这两种做法都不符合装饰器的设计理念因为它们要么修改了源代码要么改变了调用方式。4. 装饰器语法糖为了简化装饰器的使用Python 提供了语法糖defdecorator(func):definner():print(我是装饰器我增加了新的功能)func()returninnerdecorator# 装饰器语法糖defcomment():print(这是一个发表评论的功能)comment()语法糖的执行原理decorator等价于comment decorator(comment)只是写法更加简洁优雅。5. 带参数的装饰器5.1 装饰带参数的函数defdecorator(func):definner(*args,**kwargs):print(函数执行前...)resultfunc(*args,**kwargs)print(函数执行后...)returnresultreturninnerdecoratordefgreet(name):print(fHello,{name}!)returnfGreeted{name}resultgreet(Alice)print(f返回值:{result})5.2 带参数的装饰器defrepeat(times):重复执行指定次数的装饰器defouter_wrapper(func):definner(*args,**kwargs):results[]foriinrange(times):print(f第{i1}次执行:)resultfunc(*args,**kwargs)results.append(result)returnresultsreturninnerreturnouter_wrapperrepeat(times3)defsay_hello(name):print(fHello,{name}!)returnfsaid hello to{name}say_hello(Bob)6. 实际应用示例6.1 计时装饰器importtimedeftimer_decorator(func):计时装饰器defwrapper():start_timetime.time()func()end_timetime.time()print(f完成这个任务消耗的时间为{end_time-start_time:.6f}秒)returnwrappertimer_decoratordefmy_daily_work():爱你三千遍功能foriinrange(3000):print(I LOVE YOU)my_daily_work()6.2 权限验证装饰器defrequire_login(func):登录验证装饰器defwrapper(user,*args,**kwargs):ifnotuser.get(is_authenticated,False):print(错误用户未登录请先登录)returnNonereturnfunc(user,*args,**kwargs)returnwrapperrequire_logindefview_profile(user):查看用户资料print(f用户名:{user[username]})print(f邮箱:{user[email]})returnuser# 测试user1{username:alice,email:aliceexample.com,is_authenticated:True}user2{username:bob,email:bobexample.com,is_authenticated:False}view_profile(user1)# 正常执行view_profile(user2)# 提示未登录7. 多个装饰器的执行顺序defdecorator1(func):defwrapper():print(装饰器1 - 前)func()print(装饰器1 - 后)returnwrapperdefdecorator2(func):defwrapper():print(装饰器2 - 前)func()print(装饰器2 - 后)returnwrapperdecorator1decorator2defmy_function():print(原始函数)my_function()输出装饰器1 - 前 装饰器2 - 前 原始函数 装饰器2 - 后 装饰器1 - 后执行顺序从下往上从内到外。相当于my_function decorator1(decorator2(my_function))。8. 使用 functools.wraps 保留元信息使用functools.wraps可以保留原函数的名称、文档字符串等元信息importfunctoolsdefmy_decorator(func):functools.wraps(func)# 保留原函数的元信息defwrapper(*args,**kwargs):包装函数print(f调用函数:{func.__name__})returnfunc(*args,**kwargs)returnwrappermy_decoratordefexample():这是一个示例函数returnHelloprint(example.__name__)# 输出: example而不是 wrapperprint(example.__doc__)# 输出: 这是一个示例函数9. 总结装饰器是 Python 中非常强大的工具它通过闭包的机制实现了对函数的装饰功能。掌握装饰器可以帮助你写出更加模块化和可复用的代码实现横切关注点如日志、权限、性能监控的分离保持代码的整洁和可维护性遵循开放-封闭原则对扩展开放对修改封闭记住装饰器的核心不修改源代码不改变调用方式只增加功能。通过合理使用装饰器你可以大幅提升代码的质量和开发效率。扩展阅读建议深入学习functools模块的其他装饰器如lru_cache了解 Python 内置装饰器如classmethod,staticmethod,property探索装饰器在 Web 框架如 Flask、Django中的应用