装饰器本质上是一个返回函数的高阶函数,可以接收函数作为参数,并返回一个新的函数。
它允许你在不修改原函数代码的情况下,动态地给函数或方法添加额外的功能
在我们的日常使用中,装饰器一般用于:日志记录、权限认证、性能分析、缓存等场景。
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before calling the function")
func(*args, **kwargs)
print("After calling the function")
return wrapper
@my_decorator # python 中装饰器使用直接以@函数名修饰
def add(a: int, b: int) -> int:
print(a + b)
print(add(1, 2))
# 输出: Before calling the function
# 输出: 3
# 输出: After calling the function
print(add.__name__)
# 输出: wrapper (装饰器在函数上,本质返回的是wrapper, 所以函数名是wrapper)
装饰器并不是只有函数形式的,也有类形式的。
类装饰器在装饰器函数上,会返回一个类对象,这个类对象会作为装饰器函数的返回值
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Before calling the function")
result = self.func(*args, **kwargs)
print("After calling the function")
return result
@MyDecorator
def multiply(a: int, b: int) -> int:
return a * b
print(multiply(2, 3))
# 输出: Before calling the function
# 输出: 6
# 输出: After calling the function
print(type(multiply))
# 输出: (装饰器在函数上,本质返回的是MyDecorator类对象)
元数据保留是指在装饰器中保留函数的元数据,比如函数名、参数列表、返回类型等。
在函数装饰器里,可以使用@functools.wraps装饰器来保留元数据。
在类装饰器里,同样的,可以使用@functools.wraps装饰器来保留元数据,但使用上会有些不同。
# 对之前的函数装饰器进行修改
def my_decorator(func):
@functools.wraps(func) # 使用@functools.wraps来保留元数据
def wrapper(*args, **kwargs):
print("Before calling the function")
func(*args, **kwargs)
print("After calling the function")
return wrapper
print(add.__name__)
# 输出: add
# 对之前的类装饰器进行修改
class MyDecorator:
def __init__(self, func):
functools.update_wrapper(self, func) # 使用functools.update_wrapper来保留元数据
self.func = func
print(multiply.__name__)
# 输出: multiply
print(type(multiply)))
# 输出: (装饰器在函数上,本质返回的是MyDecorator类对象)
装饰器调用顺序是从下到上,从内到外。
def decorator1(func):
def wrapper(*args, **kwargs):
print("decorator1 start")
result = func(*args, **kwargs)
print("decorator1 end")
return result
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print("decorator2 start")
result = func(*args, **kwargs)
print("decorator2 end")
return result
return wrapper
@decorator1
@decorator2
def add(a: int, b: int) -> int:
print(a + b)
return (a + b)
print(add(1, 2))
# 输出:decorator1 start
# 输出:decorator2 start
# 输出:3
# 输出:decorator2 end
# 输出:decorator1 end
# 输出:3
# 不太明白的直接debug下,看看函数调用顺序
1.保持函数签名的一致性,既保持函数的参数列表和返回类型不变。
2.异常处理,装饰器内部处理的逻辑应该保持健壮性,确保发生异常也能正常输出信息和返回结果。
3.装饰器要保证无状态,确保其行为仅依赖于输入参数和被装饰的函数本身。
4.装饰器要保持可组合性,确保多个装饰器可以组合使用,并且装饰器之间的顺序不影响最终结果的正确性。
5.装饰器要保持幂等性,确保多次装饰同一个函数时,装饰器不会重复执行。