geekdoc-python-zh/docs/askpython/decorators-in-python.md

6.2 KiB
Raw Permalink Blame History

Python 中的装饰者[解释]

原文:https://www.askpython.com/python/examples/decorators-in-python

今天来说一个小众话题——Python 中的 Decorators】。这是一个简单但强大的工具顾名思义修饰功能

先决知识

在继续学习 Python 中的装饰器之前,我们需要知道几个概念,即高阶函数嵌套函数——一枚硬币的两面。

1.高阶函数

Python 有一种在其他函数之间传递和返回函数的简洁方式。许多编程语言都不支持这一点,它允许程序员进行各种各样的操作。

这就是高阶函数概念的来源——任何接受或返回另一个函数的函数都称为高阶函数。

例如:

def hof(func, num):
    int res = func(num)
    return res

大家可以注意到,hof()的第一个参数是func,是后面调用的函数。类似地,高阶函数也返回其他函数。

推荐阅读——Python 递归函数

2.嵌套函数

Python 提供的另一个通用特性是,它允许您在函数内部声明函数,这些函数被方便地称为嵌套函数。考虑这个例子:

def func(num):
    def nested_func():
        return num
    return nested_func

这里,func()是高阶函数,因为它返回另一个函数,nested_func()是嵌套函数(很明显),因为它定义在另一个函数内部。

您可以看到,嵌套函数的定义完全取决于您发送给外部函数的内容。

这用于实现封装和创建闭包,这超出了当前教程的范围。

Python 中的 Decorators 是什么?

正如我们之前讨论的,通俗地说,装饰者装饰功能。这意味着装饰者将代码或功能包装在函数周围,以增强函数的功能。

让我们看一个例子:

首先,我们来看一个简单的函数,它将两个数相加:

def sum(a, b):
    print(a + b)

Undecorated Function - Decorators in Python

A simple summation function that prints the result

现在,假设你要做一大堆这样的数学函数,它们接受两个数字,对它们执行一些数学运算,并打印它们的结果(参见 Python print

现在,假设您想在打印结果之前添加一行,告诉我们正在做什么以及正在操作什么数字。所以输出看起来像这样:

## sum of 1 and 2 ##
3

你可以在定义每个函数的同时添加这一行,但是如果函数太多,并且修饰远远不止一行,那么最好使用一个装饰器。

Python 装饰器的语法

def decorator(function):
    def wrapper(num1, num2):
        print("##", function.__name__, "of", num1, "and", num2, "##")
        function(num1, num2)
    return wrapper

理解这段代码可能有点困难,所以我们将逐行检查:

  • 这里有几件事需要注意。首先,装饰器被定义为一个函数,其行为类似于一个函数。最好把它想成一个函数。其次,也是更重要的,装饰者接受的参数是它所装饰的功能。注意装饰者的名字可以是任何东西。装饰器也可以接受多个参数,但是这是另一个讨论的主题。
  • 这可能是代码中最令人困惑的部分。装饰器必须总是返回一个为原始函数增加了一些功能的函数。这通常被称为包装器功能。这个新函数将替换原始函数,这就是为什么它必须接受与原始函数完全相同数量的参数(在本例中是两个)。所以很明显,这个装饰器不会装饰一个没有两个参数的函数,尽管有办法使用*args来解决这个问题。
  • 在我们的例子中,这将是装饰者添加到原始函数中的功能。注意,我们打印函数名和两个参数的方式与我们想要的完全一样。在此之后,我们需要执行函数,以便打印实际输出。
  • function(num1, num2):很清楚wrapper()是如何做和function()一样的事情的,但是增加了我们需要的功能,所以下一步是显而易见的。
  • return wrapper:所以基本上,decorator()从我们这里拿了一个函数,用wrapper()在它周围包装了一些装饰,然后返回wrapper(),它将替换第一个函数。因为wrapper()正在调用第一个函数并做额外的事情,它基本上是第一个函数的增强版本。

当我们看到如何使用装饰器时,剩下的就清楚了。

在 Python 中使用装饰器

现在我们已经定义了一个名为 decorator 的 decorator我们将使用它来增强三个函数——sum(我们前面已经看到过了)、 differenceproduct

@decorator
def sum(a, b):
    print(a + b)

@decorator
def difference(a, b):
    print(a - b)

@decorator
def product(a, b):
    print(a * b)

这里,符号@用来告诉 Python 下一个函数正在使用装饰器。

因此,在定义函数之后,它将被传递给装饰器,装饰器将返回它的增强版本。装饰器返回的任何函数都将替换原来的函数。

让我们看看结果:

Decorators in Python Output

注意,调用sum()将执行它的增强版本。

注意:使用装饰器会破坏函数的元数据。在我们的例子中,调用sum.__name__将返回wrapper而不是sum因为这是我们实际使用的函数。docstring 也会根据包装器拥有的 docstring 而改变。

为了避免这种情况,只需从functools导入wraps,然后在装饰器中装饰包装器,如下所示:

from functools import wraps
def decorator(function):
    @wraps(function)
    def wrapper(num1, num2):
        print("##", function.__name__, "of", num1, "and", num2, "##")
        function(num1, num2)
    return wrapper

在这种情况下,包装器本身使用函数的元数据进行修饰,以便它保留函数元,如__name__和它的 docstring。

结论

这是对如何使用装饰器以及“ @ ”符号的作用的深入解释。希望你学到了一些东西,下节课再见。

参考文献——https://www.python.org/dev/peps/pep-0318/