一、定义
装饰器是对函数的一个装饰,通过装饰器可以做到在不改变目标函数逻辑的情况下增强目标函数的能力;当调用被装饰函数时,实际上是运行装饰器返回的那个包装函数,装饰器中需要处理对目标函数的调用。
常见使用场景:
- 日志记录:自动记录函数什么时候被调用、参数是什么,返回值是什么
二、装饰器的使用
1、无参数装饰器
语法:
def 装饰器的名称(func):defwrapper(*args, **kwargs): result = func(*args, **kwargs)return resultreturn wrapperdefdecorator(func):defwrapper(*args, **kwargs):# 在这里可以添加 “额外逻辑” result = func(*args, **kwargs) # 调用原函数# 在这里可以添加 “额外逻辑”return resultreturn wrapper@decoratordeffunc(...): ...
- 内部返回一个函数,内部函数处理任意参数,调用目标函数
- 使用
*args**kwargs,让装饰器适配任意参数签名,不必逐个定义
示例:
deflog_decorator(func):defwrapper(*args, **kwargs): print("Before calling", func.__name__) result = func(*args, **kwargs) print("After calling", func.__name__)return resultreturn wrapper@log_decoratordefgreet(name): print("Hello", name)greet("Alice")# 输出Before calling greetHello AliceAfter calling greet
2、有参数装饰器
如果想要装饰器也支持参数,那么就需要在嵌套一层函数,最外层函数接收装饰器的参数。
语法:
def 最外层函数名(装饰器参数名):# 无参数装饰器defrepeat(times):defdecorator(func):defwrapper(*args, **kwargs):for _ in range(times): func(*args, **kwargs)return wrapperreturn decorator
示例:
deflogger(level):defdecorator(func):defwrapper(*args, **kwargs): print(f"[{level}] Calling {func.__name__}")return func(*args, **kwargs)return wrapperreturn decorator@logger("DEBUG")defmultiply(a, b):return a * bprint(multiply(5, 6))# 输出 [DEBUG] Calling multiply30
3、保留被装饰函数的原始信息
默认情况下,目标函数被装饰器装饰后,由于函数的执行被内部函数wrapper包裹,在获取原函数的元数据(例如函数名、文档注释)信息时,获取到的是 wrapper函数的名称和文档注释。
例如以下示例代码,我们期望获取到的函数名是 multiply,然而实际获取到的却是 wrapper:
deflogger(level):defdecorator(func):defwrapper(*args, **kwargs):"""I am a wrapper function""" print(f"[{level}] Calling {func.__name__}")return func(*args, **kwargs)return wrapperreturn decorator@logger("DEBUG")defmultiply(a, b):""" Docstring for multiply :param a: Description :param b: Description """return a * bprint(multiply.__name__)print(multiply.__doc__)print(multiply.__module__)print(multiply.__annotations__)# 输出wrapperI am a wrapper function__main__{}
那么如果我们想要获取被装饰函数的原始信息时,应该要如何实现呢?答案是使用 functools.wraps 来将原函数的元数据信息复制到包装函数上,包括但不限于:
语法:
# 1、引入 from functools import wraps# 2、内部 wrapper 函数增加 @wraps(func)
示例:
from functools import wrapsdeflogger(level):defdecorator(func): @wraps(func)defwrapper(*args, **kwargs):"""I am a wrapper function""" print(f"[{level}] Calling {func.__name__}")return func(*args, **kwargs)return wrapperreturn decorator@logger("DEBUG")defmultiply(a, b):""" Docstring for multiply :param a: Description :param b: Description """return a * bprint(multiply.__name__)print(multiply.__doc__)print(multiply.__module__)print(multiply.__annotations__)# 输出multiply Docstring for multiply :param a: Description :param b: Description__main__{}
模块名是由于测试的时候都在 main中测试
from functools import wrapsdeflog(func): @wraps(func)defwrapper(*args, **kwargs):# 打印入参 print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}") result = func(*args, **kwargs)# 打印返回值 print(f"{func.__name__} returned: {result}")return resultreturn wrapper@logdefadd(a, b):return a + bprint(add(2, 3)) # Output: 5# 输出Calling add with args: (2, 3), kwargs: {}add returned: 55