标准库 functools 中的 wrap 函数用于包装函数, 不改变原有函数的功能, 仅改变原有函数的一些属性, 例如 __name__
, __doc__
, __annotations__
等属性
先看一个简单的示例
import functools
def func_1(x: str) -> None:
"""
function
:return: None
"""
print("func_1", x)
@functools.wraps(func_1)
def demo():
"""
demo
:return: None
"""
print("demo")
print("demo name:", demo.__name__)
print("demo doc:", demo.__doc__)
print("demo annotations:", demo.__annotations__)
demo()
print("func_1 is demo's wrap:", func_1 is demo.__wrapped__)
输出
demo name: func_1
demo doc:
function
:return: None
demo annotations: {'x': <class 'str'>, 'return': None}
demo
func_1 is demo's wrap: True
使用 func_1 去包装 demo 函数, demo 函数本身的功能没变(仍打印 “demo”), 但是 __name__(函数名称), __doc__(函数文档), __annotations__(函数注释) 等属性变了, 而且 __wrapped__ 属性指向包装函数
functools.wrap
可用于装饰器的内层函数, 抵消装饰器的副作用, 因为使用装饰器后, 原函数被内层函数赋值覆盖, 函数名称等信息丢失了(装饰器仅仅是不改变函数原有功能)
def info(func):
def wrap(a, b):
return func(a, b)
return wrap
@info
def multiply(x, y):
return x * y
print("multiply name:", multiply.__name__)
打印
multiply name: wrap
使用装饰器后, multiply 指向内层函数 wrap, 所以名称变为 wrap
可以对内层函数进行包装
def info(func):
@functools.wraps(func)
def wrap(a, b):
return func(a, b)
return wrap
@info
def multiply(x, y):
return x * y
print("multiply name:", multiply.__name__)
输出
multiply name: multiply
@info 装饰器等价于 multiply = info(multiply)
, 所以 func 指向原始 multiply 函数, @functools.wraps(func)
使用原始 multiply 函数包装 wrap 函数, 随后 wrap 函数覆盖原始 multiply 函数
注意 wrap 函数与原始 multiply 函数形参必须兼容