简介
本节介绍装饰器的概念。这是一个高级主题,我们只是略作探讨。
This tutorial is from open-source community. Access the source code
💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版
本节介绍装饰器的概念。这是一个高级主题,我们只是略作探讨。
考虑一个函数。
def add(x, y):
return x + y
现在,考虑为该函数添加了一些日志记录后的情况。
def add(x, y):
print('Calling add')
return x + y
现在来看另一个同样添加了一些日志记录的函数。
def sub(x, y):
print('Calling sub')
return x - y
观察结果:这有点重复。
编写存在大量代码重复的程序通常非常烦人。编写这些程序很繁琐,而且难以维护。特别是当你决定要更改其工作方式时(例如,可能是不同类型的日志记录)。
也许你可以创建一个函数,用于生成添加了日志记录功能的函数。也就是一个包装器。
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
现在使用它。
def add(x, y):
return x + y
logged_add = logged(add)
当你调用由 logged
返回的函数时会发生什么?
logged_add(3, 4) ## 你会看到日志消息出现
这个示例展示了创建所谓的 包装器函数 的过程。
包装器是一个围绕另一个函数的函数,它带有一些额外的处理步骤,但在其他方面与原始函数的工作方式完全相同。
>>> logged_add(3, 4)
Calling add ## 额外的输出。由包装器添加
7
>>>
注意:logged()
函数创建包装器并将其作为结果返回。
在Python中,为函数添加包装器是非常常见的操作。正因如此,Python为此提供了一种特殊的语法。
def add(x, y):
return x + y
add = logged(add)
## 特殊语法
@logged
def add(x, y):
return x + y
这种特殊语法执行的步骤与上述代码完全相同。装饰器只是一种新的语法形式。它用于“装饰”函数。
装饰器还有许多比这里介绍的更微妙的细节。例如,在类中使用它们。或者对一个函数使用多个装饰器。然而,前面的示例很好地说明了它们的使用场景是如何出现的。通常,这是为了应对在广泛的函数定义中出现的重复代码。装饰器可以将这些代码移到一个集中的定义中。
如果你定义一个函数,它的名称和模块会存储在 __name__
和 __module__
属性中。例如:
>>> def add(x,y):
return x+y
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>>
在 timethis.py
文件中,编写一个装饰器函数 timethis(func)
,它会在函数周围添加一层额外的逻辑,用于打印函数执行所需的时间。为此,你需要在函数调用前后添加计时代码,如下所示:
start = time.time()
r = func(*args,**kwargs)
end = time.time()
print('%s.%s: %f' % (func.__module__, func.__name__, end-start))
以下是你的装饰器应该如何工作的示例:
>>> from timethis import timethis
>>> @timethis
def countdown(n):
while n > 0:
n -= 1
>>> countdown(10000000)
__main__.countdown : 0.076562
>>>
讨论:这个 @timethis
装饰器可以放在任何函数定义之前。因此,你可以将它用作性能调优的诊断工具。
恭喜你!你已经完成了函数装饰器实验。你可以在LabEx中练习更多实验来提升你的技能。