Python 中的装饰器

Python 有一个有趣的特性,称为装饰器,可以为现有代码添加功能。
这也称为元编程,因为程序的一部分在编译时试图修改程序的另一部分。


学习装饰器的先决条件

为了理解装饰器,我们必须首先了解 Python 中的一些基本知识。
我们必须对 Python 中的一切(是的!甚至类)都是对象这一事实感到满意。我们定义的名称只是绑定到这些对象的标识符。函数也不例外,它们也是对象(具有属性)。各种不同的名称可以绑定到同一个函数对象。
这是一个例子。

def first(msg): print(msg) first(“Hello”) second = first second(“Hello”)
输出
你好 你好
当您运行的代码,这两种功能first,并second提供相同的输出。在这里,名称first和second指代相同的函数对象。
现在事情开始变得更奇怪了。
函数可以作为参数传递给另一个函数。
如果您在 Python 中使用过map,filter和等函数reduce,那么您已经知道这一点。
这种以其他函数为参数的函数也称为高阶函数。这是此类函数的示例。

def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
我们调用函数如下。
>>> operate(inc,3) 4 >>> operate(dec,3) 2
此外,一个函数可以返回另一个函数。

def is_called(): def is_returned(): print(“Hello”) return is_returned new = is_called() # Outputs “Hello” new()

输出
你好
这里,is_returned()是一个嵌套函数,每次调用时都会定义并返回is_called()。
最后,我们必须了解Python 中的C损失


回到装饰器

函数和方法被称为可调用的,因为它们可以被调用。
事实上,任何实现特殊call()方法的对象都被称为可调用对象。所以,在最基本的意义上,装饰器是一个可调用的,它返回一个可调用的。
基本上,装饰器接收一个函数,添加一些功能并返回它。

def make_pretty(func): def inner(): print(“I got decorated”) func() return inner def ordinary(): print(“I am ordinary”)
当你在 shell 中运行以下代码时,
>>> ordinary() I am ordinary >>> # let’s decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary
在上面显示的示例中,make_pretty()是一个装饰器。在分配步骤中:
pretty = make_pretty(ordinary)
函数ordinary()被修饰,返回的函数被命名为pretty。
我们可以看到装饰器函数在原有函数的基础上增加了一些新的功能。这类似于打包礼物。装饰器充当包装器。被装饰的对象(里面的实际礼物)的性质不会改变。但现在,它看起来很漂亮(因为它被装饰了)。
通常,我们装饰一个函数并将其重新分配为,
ordinary = make_pretty(ordinary).
这是一个常见的构造,因此,Python 有一个语法来简化它。
我们可以将@符号与装饰器函数的名称一起使用,并将其放置在要装饰的函数定义之上。例如,
@make_pretty def ordinary(): print(“I am ordinary”)
相当于
def ordinary(): print(“I am ordinary”) ordinary = make_pretty(ordinary)
这只是实现装饰器的语法糖。


用参数装饰函数

上面的装饰器很简单,它只适用于没有任何参数的函数。如果我们有接受参数的函数,例如:
def divide(a, b): return a/b
这个函数有两个参数, 一种 和 乙. 我们知道如果我们传入它会报错乙 作为 0。
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): … ZeroDivisionError: division by zero
现在让我们做一个装饰器来检查这种会导致错误的情况。

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如果出现错误条件,这个新实现将返回。
>>> 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将是位置参数的元组,kwargs并将是关键字参数的字典。这种装饰器的一个例子是:
def works_for_all(func): def inner(
args, kwargs): print(“I can decorate any function”) return func(*args, kwargs) return inner


在 Python 中链接装饰器

在 Python 中可以链接多个装饰器。
也就是说,一个函数可以用不同(或相同)的装饰器多次装饰。我们只是将装饰器放在所需的函数之上。

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”)
输出
**
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 你好 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% **
上面的语法,
@star @percent def printer(msg): print(msg)
相当于
def printer(msg): print(msg) printer = star(percent(printer))
我们链接装饰器的顺序很重要。如果我们颠倒顺序,
@percent @star def printer(msg): print(msg)
输出将是:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ** 你好 ** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%