装饰器@wraps到底是个啥

引言

在前面的文章中,介绍了 Python 装饰器的各种使用方式和概念,而在使用装饰器时,@wraps 是一个非常重要的工具,它帮助我们保持被装饰函数的元数据。本文将深入探讨 @wraps 的作用及其重要性。

@wraps的作用

@wraps的定义

@wrapsfunctools 模块中的一个装饰器,它用于装饰另一个装饰器。它的主要功能是将被装饰函数的元数据(如函数名、文档字符串等)复制到装饰器内部的函数上。

什么是元数据?

元数据是关于数据的数据,可以简单理解为数据库的字段的解释。在 Python 编程中,元数据通常指的是描述函数、类或模块的属性,例如:

  • 函数名:函数的名称。
  • 文档字符串:描述函数功能的字符串,通常用于帮助文档。
  • 参数信息:函数接受的参数及其类型。
  • 返回值:函数返回的值的类型。

在 Python 中,元数据可以通过 __name____doc__ 等特殊属性访问。来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
def doc():
"""
demo func

:return: None
"""
print(1)


print(doc.__name__)
print(doc.__doc__)

运行结果为:

1
2
3
4
5
6
7
doc

demo func

:return: None


可以看到, __name__ 输出了函数的名字,而 __doc__ 属性输出了函数的文档字符串。

为什么需要@wraps

在使用装饰器时,通常会创建一个新的函数来包装原始函数,具体内容可以看上一篇文章。如果不使用 @wraps,这个新函数将会丢失原始函数的元数据。这可能会导致调试困难,因为错误信息和文档字符串将不再反映原始函数的内容。

@wraps如何影响函数的元数据

使用 @wraps 后,被装饰函数的名称、文档字符串和其他属性将被复制到装饰器内部的函数上。在使用 help() 函数或查看函数的 __name____doc__ 属性时,能够看到原始函数的信息。

wraps 示例

下面是一个不使用 @wraps 的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before calling the function.")
result = func(*args, **kwargs)
print("After calling the function.")
return result

return wrapper


@my_decorator
def say_hello(name):
"""This function greets a person."""
print(f"Hello, {name}!")


say_hello("Alice")

print(say_hello.__name__)
print(say_hello.__doc__)

运行结果为:

1
2
3
4
5
Before calling the function.
Hello, Alice!
After calling the function.
wrapper
None

可以看到,运行结果的函数名属性和函数文档字符串属性都被修改了,原函数的数据都丢失了。为什么这里会丢失呢,装饰器的原理可以看我的上一篇文章。

下面是一个使用 @wraps 的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from functools import wraps


def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Before calling the function.")
result = func(*args, **kwargs)
print("After calling the function.")
return result

return wrapper


@my_decorator
def say_hello(name):
"""This function greets a person."""
print(f"Hello, {name}!")


say_hello("Alice")

# 查看函数的元数据
print(say_hello.__name__)
print(say_hello.__doc__)

运行结果为:

1
2
3
4
5
Before calling the function.
Hello, Alice!
After calling the function.
say_hello
This function greets a person.

可以看到,在添加了装饰器后,被装饰函数的各种属性都回来了,像没有被装饰过一样。

总结

@wraps 是 Python 装饰器中一个不可或缺的工具,它确保了被装饰函数的元数据可以不被覆盖。使用 @wraps 可以提高代码的可读性和可维护性,避免在调试时遇到困扰。

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注:qrcode
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接
https://blog.d77.xyz/archives/f83ff2cd.html