跳到主要内容

Python 装饰器

提示
  1. 装饰器基础:装饰器是Python中的一种设计模式,用于通过另一个函数封装来修改函数的功能。
  2. 函数增强与简化:装饰器通过添加额外功能来增强函数,可使用@符号简化装饰过程。
  3. 扩展装饰器应用:装饰器不仅可应用于简单函数,还可处理带参数的函数,并支持多个装饰器链式调用。

在Python中,装饰器是一种设计模式,允许您通过在另一个函数中封装来修改函数的功能。

外部函数被称为装饰器,它接受原始函数作为参数并返回其修改版本。

学习装饰器的先决条件

在了解装饰器之前,我们需要理解与Python函数相关的一些重要概念。另外,请记住,Python中的一切都是对象,即使函数也是对象。

嵌套函数

我们可以在另一个函数内部包含一个函数,称为嵌套函数。例如,

def outer(x):
def inner(y):
return x + y
return inner

add_five = outer(5)
result = add_five(6)
print(result) # 打印 11

# 输出: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) # 打印 10

输出

10

在上面的例子中,calculate()函数将一个函数作为其参数。在调用calculate()时,我们传递add()函数作为参数。

calculate()函数中,参数:funcxy分别变成add46

因此,func(x, y)变成了add(4, 6),返回10

将函数作为值返回

在Python中,我们也可以将函数作为返回值返回。例如,

def greeting(name):
def hello():
return "Hello, " + name + "!"
return hello

greet = greeting("Atlantis")
print(greet()) # 打印 "Hello, Atlantis!"

# 输出:Hello, Atlantis!

在上面的例子中,return hello语句返回内部的hello()函数。这个函数现在赋值给了greet变量。

这就是为什么,当我们作为函数调用greet()时,我们得到了输出。

Python装饰器

如前所述,Python装饰器是一个接受函数并通过添加一些功能返回它的函数。

事实上,任何实现了特殊__call__()方法的对象都被称为可调用的。所以,从最基本的意义上说,装饰器是一个返回可调用对象的可调用对象。

基本上,装饰器接受一个函数,添加一些功能并返回它。

def make_pretty(func):
def inner():
print("我被装饰了")
func()
return inner

def ordinary():
print("我很普通")

# 输出:我很普通

在这里,我们创建了两个函数:

  • ordinary(),打印"我很普通"
  • make_pretty(),它接受一个函数作为参数,有一个名为inner()的嵌套函数,并返回内部函数。

我们正常调用ordinary()函数,所以我们得到输出"我很普通"。现在,让我们用装饰器函数调用它。

def make_pretty(func):
# 定义内部函数
def inner():
# 为被装饰的函数添加一些附加行为
print("我被装饰了")

# 调用原始函数
func()
# 返回内部函数
return inner

# 定义普通函数
def ordinary():
print("我很普通")

# 装饰普通函数
decorated_func = make_pretty(ordinary)

# 调用被装饰的函数
decorated_func()

输出

我被装饰了
我很普通

在上面显示的示例中,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("我被装饰了")
func()
return inner

@make_pretty
def ordinary():
print("我很普通")

ordinary()

输出

我被装饰了
我很普通

在这里,ordinary()函数使用@make_pretty语法被make_pretty()装饰器装饰,这等同于调用ordinary = make_pretty(ordinary)

带参数的函数装饰

上述装饰器很简单,它只适用于没有任何参数的函数。如果我们有像这样的函数需要参数:

def divide(a, b):
return a/b

这个函数有两个参数,ab。我们知道,如果我们传入b为0,它会报错。

现在让我们制作一个装饰器,以检查会导致错误的这种情况。

def smart_divide(func):
def inner(a, b):
print(a, "除于", b,)
if b == 0:
print("哎呀!不能相除")
return
return func(a, b)
return inner

@smart_divide
def divide(a, b):
print(a/b)

divide(2,5)

divide(2,0)

输出

2 除于 5
0.4
2 除于 0
哎呀!不能相除

在这里,当我们用参数(2,5)调用divide()函数时,smart_divide()装饰器中定义的inner()函数被调用。

这个inner()函数调用原始的divide()函数,带着参数25,并返回结果,即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
***************
%%%%%%%%%%%%%%%