python 装饰器

装饰器

装饰器本质上是一个返回函数的高阶函数,可以接收函数作为参数,并返回一个新的函数。
它允许你在不修改原函数代码的情况下,动态地给函数或方法添加额外的功能

在我们的日常使用中,装饰器一般用于:日志记录、权限认证、性能分析、缓存等场景。

简单示例

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.装饰器要保持幂等性,确保多次装饰同一个函数时,装饰器不会重复执行。

你可能感兴趣的:(python)