装饰器概念介绍

PythonPythonBeginner
立即练习

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

这种特殊语法执行的步骤与上述代码完全相同。装饰器只是一种新的语法形式。它用于“装饰”函数。

说明

装饰器还有许多比这里介绍的更微妙的细节。例如,在类中使用它们。或者对一个函数使用多个装饰器。然而,前面的示例很好地说明了它们的使用场景是如何出现的。通常,这是为了应对在广泛的函数定义中出现的重复代码。装饰器可以将这些代码移到一个集中的定义中。

练习7.10:用于计时的装饰器

如果你定义一个函数,它的名称和模块会存储在 __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中练习更多实验来提升你的技能。