[Python基础] 装饰器

Posted by Chase Shen on 2022-01-22
Estimated Reading Time 7 Minutes
Words 2k In Total
Viewed Times

装饰器(Decorator)是Python中一个非常有用的功能,它允许您修改或增强函数或方法的行为,而不需要改变其本身的代码。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。

装饰器的作用:

  1. 增强功能: 装饰器可以在不修改原函数或方法代码的情况下,给它们添加额外的功能。这符合开闭原则(对扩展开放,对修改封闭)。
  2. 代码复用: 装饰器可以在多个函数或类方法之间共享,从而避免代码重复。
  3. 清晰的代码结构: 装饰器可以帮助您将特定的功能与业务逻辑分离,使代码更加清晰和易于维护。

基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper

@my_decorator
def say_hello():
print("Hello!")

say_hello()

在这个例子中,my_decorator是一个装饰器(外部函数),它接受一个函数func并返回一个新的函数wrapper(包装器)。wrapper函数会在func之前和之后执行一些额外的代码。通过在say_hello函数前使用@my_decorator语法,我们实际上是将say_hello函数“装饰”了,使其在调用时执行额外的代码。

装饰器的工作原理:

  • 装饰器定义:my_decorator是一个接受函数func作为参数的函数。它定义了另一个内嵌的函数wrapper
  • 增加额外逻辑:在wrapper函数中,您可以在调用func()之前和之后执行一些额外的代码。
  • 返回新函数:my_decorator返回wrapper函数,而不是直接调用func

被装饰的函数:

当您使用@my_decorator修饰say_hello函数时,say_hello就相当于传递给了my_decorator,并且被替换成了my_decorator返回的wrapper函数。
因此,func()wrapper中的调用实际上是调用的say_hello函数。

结果:

当您调用say_hello()时,输出将会是:

1
2
3
Something is happening before the function is called.
Hello!
Something is happening after the function is called.

装饰器的调用方式

装饰器本身是一个函数,因此理论上可以像其他任何普通函数一样被单独调用。但是,装饰器的典型用途是作为一个注解(或“装饰”)应用于另一个函数或方法上,以修改或增强其行为。

当装饰器被定义后,您可以将其应用于函数,也可以直接调用它,并将一个函数作为参数传递给它。装饰器调用将返回一个修改后的函数。

示例

假设有以下装饰器:

1
2
3
4
5
6
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper

作为注解使用装饰器:

1
2
3
4
5
@my_decorator
def my_function():
print("Hello, world!")

my_function()

直接调用装饰器:

1
2
3
4
5
6
def my_function():
print("Hello, world!")

# 直接调用装饰器
decorated_function = my_decorator(my_function)
decorated_function()

在这两种情况下,my_function的行为都被my_decorator装饰器修改了。第一种方式使用了Python的装饰器语法(@符号),而第二种方式则是通过直接调用my_decorator函数并传递my_function作为参数来实现的。

装饰器的调用位置

装饰器可以被类外部的函数或方法调用。装饰器本身是一个独立的函数,它可以应用于任何其他函数或类方法,无论这些函数或方法定义在哪里。这意味着您可以定义一个装饰器,并将其应用于类内部的方法,也可以应用于类外部的普通函数。

类外部的装饰器应用

1
2
3
4
5
6
7
8
9
10
11
12
def my_decorator(func):
def wrapper():
print("Something before the function call.")
func()
print("Something after the function call.")
return wrapper

@my_decorator
def my_function():
print("Hello from the function!")

my_function()

应用于类内的装饰器

当装饰器应用于类的实例方法时,需要确保装饰器内的包装函数(wrapper)能够接收并传递self参数,以便正确地调用实例方法。下面是一个修改后的装饰器示例,它可以正确地处理类实例方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def my_decorator(func):
# 使包装函数能够接受任意参数
def wrapper(*args, **kwargs):
print("Something before the method call.")
result = func(*args, **kwargs) # 调用原始函数或方法
print("Something after the method call.")
return result
return wrapper

class MyClass:
@my_decorator
def my_method(self):
print("Hello from the method!")

obj = MyClass()
obj.my_method()

在这里,my_decorator装饰器被应用到MyClass类的实例方法my_method上。当my_method被调用时,self参数会被传递给wrapper函数,然后由wrapper函数传递给原始的my_method。在这个示例中,wrapper函数使用了*args**kwargs来接收任意数量的位置参数和关键字参数。这使得装饰器可以适用于多种类型的函数和方法,包括带有self参数的实例方法。

不使用嵌套函数的情况

虽然大多数情况下装饰器会使用嵌套函数,但这不是绝对必要的。在某些特殊情况下,装饰器可能不需要内部函数,尤其是当装饰器只是用于注册或标记某些东西,而不是改变函数行为时。

示例:简单的注册装饰器

1
2
3
4
5
6
7
8
9
10
11
12
registry = []

def register(func):
registry.append(func)
return func # 直接返回原始函数

@register
def my_function():
print("Hello!")

# my_function被添加到registry列表中
print(registry) # 输出:[<function my_function at 0x...>]

在这个例子中,register装饰器把函数添加到一个列表中,但并不修改函数本身。

总的来说,虽然装饰器大多数情况下会使用嵌套函数,但这不是一个硬性规定,具体取决于您要实现的功能。

装饰器的典型使用场景

使用装饰器的主要目的确实是为了增强或修改其他函数或方法的行为,所以通常它们是与实际的函数或方法结合使用的。装饰器提供了一种优雅的方式来添加功能,而无需修改原始函数或方法的代码。这使得代码更加模块化,易于维护和重用。

  • 日志记录:自动记录函数的调用细节。
  • 性能测试:测量函数执行时间。
  • 权限验证:检查调用者是否具有执行函数的权限。
  • 缓存:缓存函数的结果,避免重复计算。

无需实际函数的情况

尽管装饰器通常应用于函数或方法,但也有些特殊情况,装饰器可以用于其他用途,甚至不直接应用于特定的函数。例如,Flask框架中使用装饰器来映射URL到视图函数,但这些装饰器更多地是为了声明性地定义路由,而不是直接修改或增强特定函数的行为。

示例:Flask路由装饰器

在Flask应用中,您可以使用@app.route装饰器来指定哪个函数应该响应特定的HTTP请求和URL。

from flask import Flask

app = Flask(__name__)

# 装饰器用于映射URL
@app.route('/')
def home():
    return "Hello, World!"

if __name__ == "__main__":
    app.run()

在这个例子中,@app.route('/')装饰器不是直接增强home函数的行为,而是告诉Flask框架,当用户访问网站的根路径(即’/')时,应该调用home函数。这里的装饰器用于建立URL路径和视图函数之间的关系,而不是传统意义上修改或增强函数的行为。

这个例子展示了装饰器在Web开发框架中的另一种用法,其中装饰器作为一种配置或声明性工具,而不是直接改变函数的内部逻辑。

常见的装饰器:

  • @classmethod@staticmethod:Python内置的装饰器,用于定义类方法和静态方法。
  • Flask和Django等Web框架中的路由装饰器。
  • 登录检查、权限验证、日志记录等在Web开发和其他领域中常见的自定义装饰器。

装饰器是Python高级编程中一个强大的工具,它们增加了语言的灵活性和表达力。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !