大锤python日记(通用装饰器的实现)

23 阅读2分钟

通用装饰器示例

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 在函数调用前添加的逻辑
        print("Before the function is called.")
        
        # 调用被装饰的函数并获取返回值
        result = func(*args, **kwargs)
        
        # 在函数调用后添加的逻辑
        print("After the function is called.")
        
        # 返回被装饰函数的返回值
        return result
    
    # 返回包装过的函数
    return wrapper

这个装饰器可以被用于任何需要在函数执行前后添加逻辑的场景。例如,我们可以通过如下方式使用它来打印函数的执行时间:

import time

@my_decorator
def my_function():
    time.sleep(1)
    print("Function executed.")

my_function()

输出结果如下:

Before the function is called.
Function executed.
After the function is called.

你也可以通过向 wrapper() 函数中传递参数来为装饰器添加更多的灵活性。

关于 @wraps 的说明

functools.wraps 是一个装饰器,它可以用来简化装饰器的实现,并保留被装饰函数的元数据信息(如函数名、文档字符串、参数列表等)。

在定义装饰器时,我们通常会将被装饰函数作为参数传递给装饰器,然后返回一个新的函数。但是,如果我们不使用 wraps 装饰被返回的新函数,那么它将会丢失被装饰函数的元数据信息,这可能导致一些问题,例如在调试和文档生成时无法正确显示函数的信息。

wraps 装饰器可以解决这个问题。它将被返回的新函数的元数据信息设置为与被装饰函数相同,从而使得它们在编写和阅读代码时更容易识别和理解。具体来说,wraps 装饰器会将被返回的新函数的 __name____doc____module____annotations__ 等属性设置为被装饰函数对应的属性,这样我们就可以直接访问被装饰函数的元数据信息了。

以下是一个使用 wraps 装饰器的示例:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 在函数调用前添加的逻辑
        print("Before the function is called.")
        
        # 调用被装饰的函数并获取返回值
        result = func(*args, **kwargs)
        
        # 在函数调用后添加的逻辑
        print("After the function is called.")
        
        # 返回被装饰函数的返回值
        return result
    
    # 返回包装过的函数
    return wrapper

@my_decorator
def my_function():
    """这是一个示例函数"""
    print("Function executed.")

# 输出函数名和文档字符串
print(my_function.__name__)
print(my_function.__doc__)

输出结果如下:

my_function
这是一个示例函数

可以看到,使用 wraps 装饰器后,被装饰函数的元数据信息得以保留。