目录
- 前言
- 1. 基本知识
- 2. 无参装饰器
- 3. 有参装饰器
- 4. 多个装饰器
前言
装饰器类似Java的切点切面增强
推荐阅读:
- 详细分析Spring中的@Around注解(附Demo)
- java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
- Spring框架从入门到学精(全)
Python实战代码如下:
1. 基本知识
装饰器是Python语言中的一种高级功能,它允许将一个函数或者类“装饰”(即修改或增强)其他函数或者类的行为。装饰器本质上是一个可调用的对象,它接受一个函数或者类作为输入,并返回一个新的函数或者类
作用 | 优点 | 缺点 |
---|---|---|
代码重用: 装饰器可以用来包装重复使用的功能或逻辑,提高代码的重用性 代码简洁: 装饰器可以将一些与主要逻辑无关的代码分离出来,使主要代码更加简洁易读 横切关注点(Cross-cutting Concerns): 装饰器可以用来处理与业务逻辑无关但又必须存在于多处的功能,例如日志记录、性能检测、权限验证等 动态增加功能: 装饰器可以在不修改被装饰对象的情况下,动态地增加新的功能或行为 | 提高代码的复用性和可维护性 使代码更加简洁,易于理解 允许动态地添加或修改对象的行为,而无需修改其原始定义 | 可能会增加代码的复杂性,特别是在使用多个装饰器的情况下 装饰器可能会改变被装饰对象的原始行为,导致难以调试 |
2. 无参装饰器
通过Demo了解如何增强
# 定义一个简单的装饰器函数 simple_decorator,接受一个函数作为参数
def simple_decorator(func):# 定义一个内部函数 wrapper,用于包装被装饰的函数def wrapper():print("Before calling the function") # 在调用被装饰函数之前打印信息func() # 调用被装饰的函数print("After calling the function") # 在调用被装饰函数之后打印信息return wrapper # 返回内部函数 wrapper# 使用装饰器 simple_decorator 对 say_hello 函数进行装饰
@simple_decorator
def say_hello():print("执行一次say_hello") # 打印信息,表示 say_hello 函数被执行了一次# 调用被装饰后的 say_hello 函数,实际上会在函数执行前后打印额外的信息
say_hello()
截图如下:
3. 有参装饰器
额外添加了一个内部函数 decorator,它负责接收被装饰函数,并返回一个装饰器函数 wrapper。这种设计更符合装饰器的惯用方式,即装饰器函数接受参数(在这里是 n),然后返回一个装饰器函数,该装饰器函数再接受被装饰的函数,并返回一个新的函数(或者对原函数进行修改)
# 定义一个装饰器函数 repeat,接受一个参数 n,表示要重复执行的次数
def repeat(n):# 定义一个内部函数 decorator,该函数接受被装饰的函数作为参数 funcdef decorator(func):# 定义一个内部函数 wrapper,用于包装被装饰的函数,以实现重复执行的功能def wrapper(*args, **kwargs):# 使用 for 循环执行被装饰的函数 n 次for _ in range(n):func(*args, **kwargs) # 调用被装饰的函数return wrapper # 返回 wrapper 函数return decorator # 返回 decorator 函数# 使用装饰器 repeat(3) 对 greet 函数进行装饰,表示要重复执行 3 次
@repeat(3)
def greet(name):print(f"Hello, {name}!") # 打印输出问候语# 调用被装饰后的 greet 函数,实际上会重复打印三次问候语
greet("码农研究僧")
截图如下:
以下为错误显示:
def repeat(n):def wrapper(*args, **kwargs):func(*args, **kwargs)return wrapper@repeat(3)
def greet(name):print(f"Hello, {name}!")greet("码农研究僧")
装饰器 repeat 的内部函数 wrapper 中使用了 func(*args, **kwargs)
来调用被装饰的函数,但是在 repeat 函数内部并没有定义 func,因此会引发错误。正确的方式是在 repeat 内部定义一个参数来接收被装饰函数,并在 wrapper 中使用该参数来调用被装饰函数
4. 多个装饰器
以下代码整体逻辑如下:
greet("manong")
被 bold 装饰器装饰,变成bold(uppercase(greet))("manong")
- 首先
uppercase(greet)
被调用,得到大写的字符串"HELLO, MANONG!"
- bold 装饰器再次被调用,将大写的字符串用
<b> 和 </b>
标签包裹起来,得到<b>HELLO, MANONG!</b>
- 最终的结果被打印输出
# 定义装饰器 uppercase,将被装饰函数的返回值转换为大写
def uppercase(func):def wrapper(*args, **kwargs):print(2)# 调用被装饰函数,并将结果转换为大写result = func(*args, **kwargs)print(3)return result.upper()return wrapper# 定义装饰器 bold,将被装饰函数的返回值用 <b> 和 </b> 标签包裹起来
def bold(func):def wrapper(*args, **kwargs):print(1)# 调用被装饰函数,并用 <b> 和 </b> 标签包裹返回结果result = func(*args, **kwargs)print(4)return f"<b>{result}</b>"return wrapper# 先应用 bold 装饰器,再应用 uppercase 装饰器
@bold
@uppercase
def greet(name):return f"Hello, {name}!"# 调用 greet 函数,并输出结果
print(greet("manong"))
截图如下:
当函数 greet 被装饰时, uppercase 装饰器会先执行,然后是 bold 装饰器。这意味着 uppercase 装饰器的 wrapper 函数会先被调用,然后是 bold 装饰器的 wrapper 函数