@wraps(func)
是 Python 标准库 functools
模块中的一个装饰器,它的作用是保留被装饰函数(func
)的元信息。具体来说,它会将被装饰函数的 __name__
、__doc__
、__module__
等属性复制到装饰器函数中,从而避免装饰器“掩盖”原函数的元信息。
为什么需要 @wraps(func)
?
在 Python 中,装饰器本质上是一个高阶函数,它接受一个函数作为参数并返回一个新的函数。当我们使用装饰器时,原始函数会被替换为装饰器返回的新函数。这会导致一个问题:原始函数的元信息(如函数名、文档字符串等)会被新函数覆盖。
例如:
def my_decorator(func):
def wrapper(*args, **kwargs):
"""Wrapper function"""
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def say_hello():
"""Greet the world."""
print("Hello!")
print(say_hello.__name__) # 输出: wrapper
print(say_hello.__doc__) # 输出: Wrapper function
在这个例子中,say_hello
函数被 my_decorator
装饰后,它的 __name__
和 __doc__
属性变成了 wrapper
函数的属性,而不是原始的 say_hello
函数的属性。
使用 @wraps(func)
解决问题
为了解决这个问题,我们可以使用 @wraps(func)
装饰器来保留原始函数的元信息:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper function"""
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def say_hello():
"""Greet the world."""
print("Hello!")
print(say_hello.__name__) # 输出: say_hello
print(say_hello.__doc__) # 输出: Greet the world.
在这个改进的版本中,@wraps(func)
将 say_hello
的 __name__
和 __doc__
属性复制到了 wrapper
函数中,因此原始函数的元信息得以保留。
@wraps(func)
的作用总结
- 保留函数名:
__name__
属性会被设置为原始函数的名称,而不是装饰器内部函数的名称。 - 保留文档字符串:
__doc__
属性会被设置为原始函数的文档字符串。 - 保留模块信息:
__module__
属性会被设置为原始函数所在的模块。 - 保留其他属性:
__annotations__
、__dict__
等属性也会被复制到装饰器函数中。
使用场景
@wraps(func)
通常用于编写装饰器时,尤其是当你希望装饰后的函数仍然保留原始函数的元信息时。这在调试、文档生成和代码自省(如使用 help()
函数)时非常有用。
示例代码
from functools import wraps
def log_function_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned: {result}")
return result
return wrapper
@log_function_call
def add(a, b):
"""Add two numbers."""
return a + b
print(add.__name__) # 输出: add
print(add.__doc__) # 输出: Add two numbers.
add(2, 3) # 输出: Calling add with args: (2, 3), kwargs: {}
# add returned: 5
在这个例子中,@wraps(func)
确保了 add
函数的元信息被正确保留,即使它被 log_function_call
装饰器装饰。