Python中的装饰器

一、装饰器的作用

装饰器是Python中一种强大的编程工具,它允许我们在不修改原始函数代码的情况下,动态地增加功能或修改函数行为。装饰器提供了一种简洁而优雅的方式来修改、扩展或包装函数,使代码更具可读性和可维护性。

装饰器的主要作用包括:

  • 添加额外的功能或逻辑,如日志记录、性能分析、输入验证等。
  • 修改函数的行为,如缓存结果、重试机制等。
  • 分离关注点,将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来。

二、装饰器的原理

在理解装饰器之前,我们需要了解一些Python的基础知识:函数是一等公民(first-class citizen),即函数可以被赋值给变量、作为参数传递和作为返回值返回。装饰器利用了这个特性。

装饰器本质上是一个函数,它接受一个函数作为参数,然后返回一个新的函数。这个新函数通常包装了原始函数,即在原始函数的前后添加了一些额外的代码或逻辑。通过将装饰器应用于函数,我们可以在不修改函数定义的情况下,动态地修改函数的行为。

三、装饰器的使用方法

例子1:日志记录

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def add(a, b): 	# 被装饰的函数
    return a + b

result = add(2, 3)  # 输出:Calling function: add
print(result)  # 输出:5

在上述例子中,log_decorator是一个装饰器函数,它接受一个函数作为参数,并返回一个新的函数 wrapper。在 wrapper函数中,我们可以在调用原始函数之前或之后添加额外的代码。通过将装饰器应用于 add函数,我们实现了在调用 add函数时打印函数名的功能。

你可能认为,如果不用装饰器,实现这个功能也不难,比如:

def add(a, b):
    print(f"Calling function: add")
    return a + b

result = add(2, 3)
print(result)

然而,这只是一个函数,如果有多个函数都需要被加入这个功能,那修改起来就很麻烦,如果使用装饰器,就很容易,例如:

@log_decorator
def minus(a, b): # 被装饰的函数
    return a - b

@log_decorator
def multiply(a, b): # 被装饰的函数
    return a * b

@log_decorator
def devide(a, b): # 被装饰的函数
    return a / b

这样,在不改动原函数的情况下,所有函数都实现了记录日志的功能。下面再看一个更复杂的例子。

例子2:性能分析

import time

def performance_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"运行时间 {func.__name__}: {end_time - start_time} 秒")
        return result
    return wrapper

@performance_decorator
def f():
    time.sleep(2.5)

result = f()  # 输出:运行时间 f: 2.500232219696045 秒


@performance_decorator
def g(): # 被装饰的函数
    time.sleep(3.3)

result = g()  # 输出:运行时间 g: 3.301257610321045 秒

在上述例子中,performance_decorator是一个装饰器函数,它用来计算函数的执行时间。通过将装饰器应用于其它函数,我们可以方便地测量函数的执行时间。fg 函数并没有实际内容,只是用 sleep 来模拟运行时间。对于每个函数,都输出了正确的测量结果。对于更多的函数,也可以用这个装饰器来完成。

例子3. 带参数的装饰器

当装饰器有参数时,通常需要定义一个外层函数来接收参数,并在外层函数内再定义一个真正的装饰器函数。以下是一个有参数的装饰器案例,用于限制函数执行的次数:

def limit_execution_times(max_executions):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if wrapper.execution_count < max_executions:
                result = func(*args, **kwargs)
                wrapper.execution_count += 1
                return result
            else:
                print(f"函数 '{func.__name__}' 已达到最大执行次数.")
                return None

        wrapper.execution_count = 0
        return wrapper
    return decorator

# 使用装饰器限制函数最多执行3次
@limit_execution_times(3)
def print_hello(): # 被装饰的函数
    print("Hello, world!")

print_hello()  # 输出:Hello, world!
print_hello()  # 输出:Hello, world!
print_hello()  # 输出:Hello, world!
print_hello()  # 输出:函数 'print_hello' 已达到最大执行次数.

在这个例子中,我们定义了一个带参数的装饰器 limit_execution_times,它接收一个参数 max_executions,表示函数的最大执行次数。在装饰器内部,我们使用一个内部函数 wrapper来包装原始函数,同时添加了一个 execution_count属性来记录函数执行的次数。每次调用被装饰的函数时,都会检查执行次数是否超过最大执行次数,如果未超过,则继续执行原始函数;如果已达到最大执行次数,则输出相应提示信息,并不再执行原始函数。这样,我们就实现了一个带参数的装饰器,用于限制函数的执行次数。

四、装饰器的使用场合

装饰器在很多场景下都可以发挥作用,例如:

  • 记录日志或统计日志;
  • 对函数进行输入验证或参数校验;
  • 实现缓存机制,避免重复计算;
  • 实现权限控制或身份验证;
  • 实现性能分析或调试;
  • 实现事务处理或错误处理等。

装饰器可以帮助我们将这些横切关注点从核心业务逻辑中分离出来,提高代码的可读性、可维护性和复用性。

posted @ 2023-12-01 11:13  AiniIT琦玉  阅读(13)  评论(0编辑  收藏  举报