Python 装饰器是强大的工具,可帮助您生成干净、可重用和可维护的代码。在不影响质量的情况下,用更少的代码做更多的事情
我等了很久才想了解这些抽象,现在我已经有了扎实的理解,我写这个故事作为实用指南,也可以帮助您掌握这些对象背后的概念。
今天没有大的介绍或冗长的理论定义。
这篇文章是我经常在我的项目中使用的 12 个有用的装饰器的文档列表,以使用额外的功能扩展我的代码。我们将深入研究每个装饰器,查看代码并尝试一些动手示例。
如果您是 Python 开发人员,这篇文章将使用有用的脚本扩展您的工具箱,以提高您的工作效率并避免代码重复。
话不多说,我建议我们现在就进入代码💻
1 — @logger(开始)✏️
如果您不熟悉装饰器,您可以将它们视为将函数作为输入并在不改变其主要用途的情况下扩展其功能的函数。
让我们从一个简单的装饰器开始,它通过记录函数开始和结束执行的时间来扩展函数。
被修饰的函数的结果如下所示:
some_function(args)
# ----- some_function: 开始 -----
# some_function 执行
# ----- some_function: 结束 -----
编写 decroator
,您首先必须选择一个合适的名称:我们称它为logger
logger
是一个函数,它将一个函数作为输入并返回一个函数作为输出。输出函数通常是输入的扩展版本。start
在我们的例子中,我们希望输出函数用and
语句包围输入函数的调用end
。
由于我们不知道输入函数使用什么参数,我们可以使用*args
和**kwargs
从包装函数传递它们。这些表达式允许传递任意数量的位置参数和关键字参数。
下面是装饰器的一个简单实现logger
:
def logger(function):
def wrapper(*args, **kwargs):
print(f"----- {function.__name__}: start -----")
output = function(*args, **kwargs)
print(f"----- {function.__name__}: end -----")
return output
return wrapper
现在您可以将记录器应用到some_function或任何其他功能。
decorated_function = logger(some_function)
Python 为此提供了更 Pythonic 的语法,它使用@符号。
@logger
def some_function ( text ):
print (text)
some_function( "first test" )
# ----- some_function: start -----
# first test
# ----- some_function: end -----
some_function( "second test" )
# ----- some_function: start -----
# second test
# ----- some_function: end -----
2 — @wraps 🎁
装饰器将包装器函数更新为看起来像原始函数并继承它的名称和属性。
为了理解它的@wraps作用以及为什么要使用它,让我们把前面的装饰器应用到一个简单的函数中,该函数将两个数字相加。
装饰器还没有使用@wraps
def logger(function):
def wrapper(*args, **kwargs):
"""wrapper documentation"""
print(f"----- {function.__name__}: start -----")
output = function(*args, **kwargs)
print(f"----- {function.__name__}: end -----")
return output
return wrapper
@logger
def add_two_numbers(a, b):
"""this function adds two numbers"""
return a + b
add_two_numbers
如果我们通过调用__name__
和属性来检查修饰函数的名称和文档__doc__
,我们会得到……不自然(但仍是预期的)结果:
add_two_numbers.__name__
'包装器'
add_two_numbers.__doc__
'包装器文档' 我们取而代之的是包装器名称和文档 ⚠️
这是不希望的结果。我们希望保留原始函数的名称和文档。这时候@wraps装饰器 就派上用场了。
您所要做的就是装饰包装函数。
from functools import wraps
def logger(function):
@wraps(function)
def wrapper(*args, **