企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# Python 装饰器 > 原文: [https://www.programiz.com/python-programming/decorator](https://www.programiz.com/python-programming/decorator) #### 装饰器接受一个函数,添加一些功能并返回它。 在本教程中,您将学习如何创建装饰器以及为什么要使用它。 ## Python 中的装饰器 Python 有一个有趣的函数,称为**装饰器**,可为现有代码添加功能。 这也称为**元编程**,因为程序的一部分试图在编译时修改程序的另一部分。 * * * ## 学习装饰器的先决条件 为了了解装饰器,我们必须首先了解 Python 的一些基本知识。 我们必须对以下事实感到满意:Python 中的所有内容(是!甚至是类)都是[对象](/python-programming/class)。 我们定义的名称只是绑定到这些对象的标识符。 [函数](/python-programming/function)也不例外,它们也是对象(带有属性)。 各种不同的名称可以绑定到同一函数对象。 这是一个例子。 ```py def first(msg): print(msg) first("Hello") second = first second("Hello") ``` **输出** ```py Hello Hello ``` 当您运行代码时,两个函数`first`和`second`给出相同的输出。 在这里,名称`first`和`second`指的是同一函数对象。 现在事情开始变得怪异了。 可以将函数作为参数传递给另一个函数。 如果您在 Python 中使用过`map`,`filter`和`reduce`之类的函数,那么您已经知道这一点。 将其他函数作为参数的此类函数也称为**高阶函数**。 这是这种函数的一个例子。 ```py def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result ``` 我们调用函数如下。 ```py >>> operate(inc,3) 4 >>> operate(dec,3) 2 ``` 此外,一个函数可以返回另一个函数。 ```py def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new() ``` **输出**: ```py Hello ``` 在这里,`is_returned()`是一个嵌套函数,每次调用`is_called()`时都会定义并返回该函数。 最后,我们必须了解 Python 中的 C [losures。](/python-programming/closure) * * * ## 回到装饰器 函数和方法被称为**可调用**,因为它们可以被调用。 实际上,任何实现特殊`__call__()`方法的对象都称为可调用对象。 因此,从最基本的意义上讲,装饰器是可调用的,可返回可调用的。 基本上,装饰器接受一个函数,添加一些功能并返回它。 ```py def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary") ``` 在 shell 中运行以下代码时, ```py >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary ``` 在上面显示的示例中,`make_pretty()`是装饰器。 在分配步骤中: ```py pretty = make_pretty(ordinary) ``` 装饰了函数`ordinary()`,并将返回的函数命名为`pretty`。 我们可以看到装饰器函数在原始函数中添加了一些新功能。 这类似于包装礼物。 装饰器充当包装器。 装饰的对象(内部实际礼物)的性质不会改变。 但是现在,它看起来很漂亮(因为它已经被装饰了)。 通常,我们装饰一个函数并将其重新分配为 ```py ordinary = make_pretty(ordinary). ``` 这是一个常见的构造,因此,Python 具有简化此语法的语法。 我们可以将`@`符号与装饰器函数的名称一起使用,并将其放置在要装饰的函数的定义上方。 例如, ```py @make_pretty def ordinary(): print("I am ordinary") ``` 相当于 ```py def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary) ``` 这只是实现装饰器的语法糖。 * * * ## 带参装饰器 上面的装饰器很简单,并且只能用于没有任何参数的函数。 如果我们的函数具有以下参数: ```py def divide(a, b): return a/b ``` 该函数具有两个参数,`a`和`b`。 我们知道,如果我们将`b`传递为 0,将会产生错误。 ```py >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): ... ZeroDivisionError: division by zero ``` 现在,让我们做一个装饰器来检查这种情况是否会导致错误。 ```py def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b) ``` 如果出现错误情况,此新实现将返回`None`。 ```py >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide ``` 通过这种方式,我们可以修饰带有参数的函数。 敏锐的观察者会注意到,装饰器内部嵌套的`inner()`函数的参数与其装饰的函数的参数相同。 考虑到这一点,现在我们可以使通用装饰器可以使用任意数量的参数。 在 Python 中,此魔术是通过`function(*args, **kwargs)`完成的。 这样,`args`将是位置参数的[元组](/python-programming/tuple),`kwargs`将是关键字参数的[字典](/python-programming/dictionary)。 这样的装饰器的一个例子是: ```py def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner ``` * * * ## Python 中的链接装饰器 可以在 Python 中链接多个装饰器。 这就是说,一个函数可以用不同(或相同)的装饰器多次装饰。 我们只需将装饰器放置在所需函数之上。 ```py def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello") ``` **输出**: ```py ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** ``` 上面的语法, ```py @star @percent def printer(msg): print(msg) ``` is equivalent to ```py def printer(msg): print(msg) printer = star(percent(printer)) ``` 链接装饰器的顺序很重要。 如果我们将顺序颠倒为 ```py @percent @star def printer(msg): print(msg) ``` 输出为: ```py %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ```