在 Python 中,装饰器是一种设计模式,它允许你通过将一个函数包装在另一个函数中来修改其功能。
外部函数被称为装饰器,它接受原始函数作为参数并返回其修改版本。
学习装饰器的先决条件
在学习装饰器之前,我们需要理解一些与 Python 函数相关的重要概念。另外,请记住,Python 中的一切都是对象,即使函数也是对象。
嵌套函数
我们可以将一个函数包含在另一个函数中,这被称为嵌套函数。例如:
def outer(x):
def inner(y):
return x + y
return inner
add_five = outer(5)
result = add_five(6)
print(result) # prints 11
# Output: 11
在这里,我们在 outer()
函数内部创建了 inner()
函数。
将函数作为参数传递
在 Python 中,我们可以将一个函数作为参数传递给另一个函数。例如:
def add(x, y):
return x + y
def calculate(func, x, y):
return func(x, y)
result = calculate(add, 4, 6)
print(result) # prints 10
输出
10
在上面的示例中,calculate()
函数接受一个函数作为其参数。在调用 calculate()
时,我们传递了 add()
函数作为参数。
在 calculate()
函数中,参数:func
、x
、y
分别变为 add
、4
和 6
。
因此,func(x, y)
变为 add(4, 6)
,返回 10。
将函数作为值返回
在 Python 中,我们也可以将函数作为返回值返回。例如:
def greeting(name):
def hello():
return "Hello, " + name + "!"
return hello
greet = greeting("Atlantis")
print(greet()) # prints "Hello, Atlantis!"
# Output: Hello, Atlantis!
在上面的示例中,return hello
语句返回内部的 hello()
函数。现在,这个函数被赋值给 greet 变量。
这就是为什么当我们像函数一样调用 greet()
时,会得到输出。
Python装饰器
如前所述,Python 装饰器是一个接受函数并添加一些功能后返回该函数的函数。
事实上,任何实现特殊 __call__()
方法的对象都被称为可调用对象。所以,从最基本的意义上讲,装饰器是一个返回可调用对象的可调用对象。
基本上,装饰器接受一个函数,添加一些功能并返回它。
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
# Output: I am ordinary
在这里,我们创建了两个函数
ordinary()
打印"I am ordinary"
make_pretty()
接受一个函数作为参数,并有一个名为inner()
的嵌套函数,并返回内部函数。
我们正常调用 ordinary()
函数,因此我们得到输出 "I am ordinary"
。现在,让我们使用装饰器函数调用它。
def make_pretty(func):
# define the inner function
def inner():
# add some additional behavior to decorated function
print("I got decorated")
# call original function
func()
# return the inner function
return inner
# define ordinary function
def ordinary():
print("I am ordinary")
# decorate the ordinary function
decorated_func = make_pretty(ordinary)
# call the decorated function
decorated_func()
输出
I got decorated I am ordinary
在上面显示的示例中,make_pretty()
是一个装饰器。注意代码,
decorated_func = make_pretty(ordinary)
- 我们现在将
ordinary()
函数作为参数传递给make_pretty()
。 make_pretty()
函数返回内部函数,现在它被赋值给 decorated_func 变量。
decorated_func()
在这里,我们实际上调用了 inner()
函数,我们在其中打印
装饰器与 @ 符号
Python 提供了一种更优雅的方式来使用 @
符号实现此功能,而不是将函数调用赋值给一个变量。例如:
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
@make_pretty
def ordinary():
print("I am ordinary")
ordinary()
输出
I got decorated I am ordinary
在这里,ordinary()
函数使用 @make_pretty
语法被 make_pretty()
装饰器装饰,这等效于调用 ordinary = make_pretty(ordinary)
。
用参数装饰函数
上面的装饰器很简单,它只适用于没有任何参数的函数。如果我们的函数接受参数,例如:
def divide(a, b):
return a/b
这个函数有两个参数,a
和 b
。我们知道如果我们将 b 作为 0 传入,它将产生错误。
现在让我们创建一个装饰器来检查这种会导致错误的情况。
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)
divide(2,5)
divide(2,0)
输出
I am going to divide 2 and 5 0.4 I am going to divide 2 and 0 Whoops! cannot divide
在这里,当我们使用参数 (2,5) 调用 divide()
函数时,会调用 smart_divide()
装饰器中定义的 inner()
函数。
此 inner()
函数使用参数 2 和 5 调用原始 divide()
函数并返回结果,即 0.4。
类似地,当我们使用参数 (2,0) 调用 divide()
函数时,inner()
函数会检查 b
是否等于 0,并在返回 None
之前打印一条错误消息。
在 Python 中链式装饰器
在 Python 中可以链式使用多个装饰器。
要在 Python 中链式使用装饰器,我们可以通过将多个装饰器一个接一个地应用于单个函数,最里面的装饰器首先应用。
def star(func):
def inner(*args, **kwargs):
print("*" * 15)
func(*args, **kwargs)
print("*" * 15)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 15)
func(*args, **kwargs)
print("%" * 15)
return inner
@star
@percent
def printer(msg):
print(msg)
printer("Hello")
输出
*************** %%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%% ***************
上述语法,
@star
@percent
def printer(msg):
print(msg)
等同于
def printer(msg):
print(msg)
printer = star(percent(printer))
我们链式装饰器的顺序很重要。如果我们颠倒顺序,如下:
@percent
@star
def printer(msg):
print(msg)
输出将是
%%%%%%%%%%%%%%% *************** Hello *************** %%%%%%%%%%%%%%%
另请阅读