python装饰器
1. 装饰器的定义
就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
2.装饰器代码
# 添加一个登录验证的功能 def decorator(fn): def inner(): print("请先登录....") fn() return inner def comment(): print("发表评论") # 使用装饰器来装饰函数 comment = decorator(comment) comment()
说明:
- 闭包函数有且只有一个函数类型参数,这样定义的闭包函数才是装饰器。
- 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
执行结果:
请先登录....
发表评论
3. 装饰器的语法糖写法
Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
# 添加一个登录验证的功能 def decorator(fn): def inner(): print("请先登录....") fn() return inner @decorator def comment(): print("发表评论") # 使用装饰器来装饰函数 comment()
说明:
- @decorator 等价于 comment = decorator(comment)
- 装饰器的执行时间是加载模块时立即执行。
- 装饰完后,comment方法实际上就是装饰器返回的inner方法
4. 通用装饰器
# 通用装饰器 def decorator(fn): def inner(*args, **kwargs): print("请先登录....") res = fn(*args, **kwargs) return res return inner @decorator def comment(): print("发表评论") @decorator def calculate(a, b): return a + b # 使用装饰器来装饰函数 comment() print(calculate(1,2))
执行结果:
请先登录.... 发表评论 请先登录.... 3 Process finished with exit code 0
说明:
- 同用装饰器就是内部函数使用可变参数且有返回值的装饰器,因为可变参数可以接收任何参数
-
res = fn(*args, **kwargs) 内部函数这里的*args, **kwargs,是将元组args解包为位置参数,将字典kwargs解包为关键字参数
5. 多个装饰器的使用:
def make_div(func): """对被装饰的函数的返回值 div标签""" def inner(*args, **kwargs): return "<div>" + func(*args, **kwargs) + "</div>" return inner def make_p(func): """对被装饰的函数的返回值 p标签""" def inner(*args, **kwargs): return "<p>" + func(*args, **kwargs) + "</p>" return inner # 装饰过程: 1 content = make_p(content) 2 content = make_div(content) # content = make_div(make_p(content)) @make_div @make_p def content(): return "人生苦短" result = content() print(result)
执行结果:
<div><p>人生苦短</p></div>
Process finished with exit code 0
说明:
- 多个装饰器装饰过程为由内到外装饰,上面的例子先make_p装饰,然后用make_div装饰
6. 带参数的装饰器
# 添加输出日志的功能 def logging(flag): def decorator(fn): def inner(num1, num2): if flag == "+": print("--正在努力加法计算--") elif flag == "-": print("--正在努力减法计算--") result = fn(num1, num2) return result return inner # 返回装饰器 return decorator # 使用装饰器装饰函数 @logging("+") def add(a, b): result = a + b return result @logging("-") def sub(a, b): result = a - b return result result = add(1, 2) print(result) result = sub(1, 2) print(result)
说明:
- 使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用
7. 类装饰器
装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。
类装饰器示例代码:
class Check(object): def __init__(self, fn): # 初始化操作在此完成 self.__fn = fn # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。 def __call__(self, *args, **kwargs): # 添加装饰功能 print("请先登陆...") self.__fn() @Check def comment(): print("发表评论") comment()
说明:
- @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
- 装饰器执行完comment变量就变成一个Check的一个实例对象
- 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
- 在call方法里进行对fn函数的装饰,可以添加额外的功能。
执行结果:
请先登陆...
发表评论
@wraps()语法糖(了解)
因为装饰器实质是就是一个函数,是一个被修饰过函数,他与原来未被修饰的函数是两个不同的函数对象。
所以,这个装饰器丢失了原来函数对象的一些属性,比如:__name__,__doc__等属性。使用wraps语法糖可以保留这些属性。
2. 小结
- 想要让类的实例对象能够像函数一样进行调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable)
- 类装饰器装饰函数功能在call方法里面进行添加
扩展:其实普通函数里面也实现了call方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构