Python 程序语言设计模式思路-结构型模式:装饰器讲解及利用Python装饰器模式实现高效日志记录和性能测试

公众号:人生只不过是一场投资

  • 装饰器模式也是结构型模式
  • 上一个例子中,朋友反馈理解难度对于他来说较高,所以,这篇文章继续说一说这结构型模式-装饰器

引言

在软件开发中,设计模式是一套被反复使用、经过分类和总结的代码设计经验。装饰器模式(Decorator Pattern)是结构型设计模式之一,用于动态地向对象添加额外的职责,而不影响其他对象。这种模式通过使用装饰器函数,允许你在保持原始功能的基础上,灵活地扩展代码的行为。

应用领域

装饰器模式在以下场景中特别有用:

  1. 日志记录:在函数执行前后添加日志记录功能。
  2. 性能测试:测量函数的执行时间。
  3. 权限验证:在执行函数前进行权限检查。
  4. 缓存:缓存函数的计算结果,以提高性能。
  5. 输入校验:在函数执行前对输入参数进行检查。

实例及详解

我们以一个简单的日志记录装饰器为例,来讲解装饰器模式的实现。

基础函数

首先,定义一个基础函数,用于模拟业务逻辑:

def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

在这个例子中,我们定义了一个简单的函数 say_hello,它接受一个参数 name,并打印一条问候信息。调用 say_hello("Alice") 会输出 Hello, Alice!

装饰器函数

接下来,定义一个装饰器函数,用于在执行 say_hello 函数前后记录日志:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__} with arguments {args} and {kwargs}")
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} finished executing")
        return result
    return wrapper

这个装饰器函数 log_decorator 有几点需要注意:

  1. 接受一个函数作为参数:装饰器函数 log_decorator 接受一个函数 func 作为参数。
  2. 定义一个内部函数 wrapper:这个内部函数 wrapper 实现了装饰功能。在调用原始函数 func 前后,添加了日志记录。
  3. 参数传递:wrapper 函数使用 *args**kwargs 传递所有参数,以保证可以接受任意数量的位置参数和关键字参数。
  4. 调用原始函数:result = func(*args, **kwargs) 这行代码调用了原始函数,并将结果存储在 result 变量中。
  5. 返回结果:wrapper 函数返回原始函数的结果。
  6. 返回 wrapper 函数:log_decorator 最后返回 wrapper 函数,从而完成对原始函数的装饰。

应用装饰器

使用装饰器语法糖 @ 来应用装饰器函数:

@log_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

通过在 say_hello 函数定义之前添加 @log_decorator,我们将 say_hello 函数传递给 log_decorator,并用 log_decorator 返回的 wrapper 函数替换 say_hello。这意味着,每次调用 say_hello 实际上是在调用 wrapper 函数。

运行上述代码,输出如下:

Calling function say_hello with arguments ('Alice',) and {}
Hello, Alice!
Function say_hello finished executing

从输出中可以看到,装饰器成功地在调用 say_hello 函数前后记录了日志。

详细说明

  1. 调用函数前日志:在执行原始函数前,wrapper 打印了一条日志,显示函数名称和传递的参数。
  2. 调用原始函数:wrapper 调用了原始的 say_hello 函数,输出 Hello, Alice!
  3. 调用函数后日志:在原始函数执行完毕后,wrapper 打印了一条日志,表示函数执行结束。

优点

  1. 增强功能:无需修改原始函数代码,即可为其添加新功能。
  2. 保持单一职责:通过分离功能,保持每个函数职责单一,有利于代码的维护和扩展。
  3. 复用性高:一个装饰器可以应用于多个不同的函数,提高代码的复用性。

缺点

  1. 调试复杂:过多的装饰器嵌套会增加调试难度,难以追踪函数的执行路径。
  2. 性能开销:由于每个装饰器都会引入额外的函数调用,可能会带来性能上的开销。
  3. 可读性下降:对于不熟悉装饰器模式的开发者来说,理解代码可能会有一定的困难。

结论

装饰器模式是Python中强大而灵活的设计模式,广泛应用于各种场景中。通过使用装饰器,我们可以在不修改现有代码的基础上,为函数动态添加新功能。尽管有一些潜在的缺点,如调试复杂和性能开销,但合理使用装饰器能够显著提高代码的可维护性和扩展性。在实际开发中,理解并熟练运用装饰器模式,将帮助你编写出更简洁、模块化和可扩展的代码。

日志记录应用

日志配置

首先,我们需要配置 logging 库,将日志记录到一个 log.txt 文件中:

import logging

# 配置日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    filename='log.txt',
                    filemode='a')

logger = logging.getLogger(__name__)

定义装饰器

定义一个装饰器函数,用于在函数执行前后记录日志:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logger.info(f"Calling function {func.__name__} with arguments {args} and {kwargs}")
        result = func(*args, **kwargs)
        logger.info(f"Function {func.__name__} finished executing")
        return result
    return wrapper

示例一:函数使用装饰器

使用装饰器语法糖 @ 来应用装饰器函数:

@log_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

运行结果

运行上述代码,控制台输出:

Hello, Alice!

同时,log.txt 文件中记录了以下日志:

2024-05-19 18:56:40,998 - __main__ - INFO - Calling function say_hello with arguments ('Alice',) and {}
2024-05-19 18:56:40,998 - __main__ - INFO - Function say_hello finished executing

示例二:类使用装饰器

在这个示例中,我们将为一个类的方法添加日志记录功能。

定义类并应用装饰器

定义一个类,并使用装饰器记录方法的日志:

class Greeter:
    @log_decorator
    def greet(self, name):
        print(f"Hello, {name}!")

greeter = Greeter()
greeter.greet("Bob")

运行结果

运行上述代码,控制台输出:

Hello, Bob!

同时,log.txt 文件中记录了以下日志:

2024-05-19 18:58:07,830 - __main__ - INFO - Calling function greet with arguments (<__main__.Greeter object at 0x000001C7991944D0>, 'Bob') and {}
2024-05-19 18:58:07,830 - __main__ - INFO - Function greet finished executing

详细说明

  1. 日志配置:我们使用 logging.basicConfig 配置日志记录。日志级别设置为 INFO,日志消息格式包括时间戳、记录器名称、日志级别和消息内容。日志文件名为 log.txt,以追加模式打开。
  2. 装饰器函数:log_decorator 接受一个函数或方法作为参数,并返回一个包装函数 wrapper。在 wrapper 中,记录函数或方法调用的日志,并在执行完毕后记录日志。
  3. 函数示例:我们定义了一个简单的函数 say_hello,并使用装饰器 log_decorator 为其添加日志记录功能。调用 say_hello("Alice") 会在控制台输出问候信息,并在 log.txt 文件中记录函数调用的日志。
  4. 类示例:我们定义了一个类 Greeter,其方法 greet 使用装饰器 log_decorator 添加日志记录功能。调用 greeter.greet("Bob") 会在控制台输出问候信息,并在 log.txt 文件中记录方法调用的日志。

通过以上两个示例,展示了如何使用 logging 库和装饰器模式来记录日志,并将日志保存到 txt 文件中。这种方法提高了代码的可维护性和功能扩展性。

性能测试与日志记录场景应用

定义装饰器

定义一个装饰器函数,用于在函数执行前后测量并记录执行时间:

import time
import logging

# 配置日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    filename='performance_log.txt',
                    filemode='a')

logger = logging.getLogger(__name__)

def time_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        execution_time = end_time - start_time
        logger.info(f"Function {func.__name__} executed in {execution_time:.4f} seconds")
        return result
    return wrapper

示例一:函数使用装饰器

使用装饰器语法糖 @ 来应用装饰器函数:

@time_decorator
def say_hello(name):
    time.sleep(1)  # 模拟耗时操作
    print(f"Hello, {name}!")

say_hello("Alice")

运行结果

运行上述代码,控制台输出:

Hello, Alice!

同时,performance_log.txt 文件中记录了以下日志:

2024-05-19 19:10:30,359 - __main__ - INFO - Function say_hello executed in 1.0008 seconds

示例二:类使用装饰器

定义一个类,并使用装饰器测量方法的执行时间:

class Greeter:
    @time_decorator
    def greet(self, name):
        time.sleep(2)  # 模拟耗时操作
        print(f"Hello, {name}!")

greeter = Greeter()
greeter.greet("Bob")

运行结果

运行上述代码,控制台输出:

Hello, Bob!

同时,performance_log.txt 文件中记录了以下日志:

2024-05-19 19:11:20,958 - __main__ - INFO - Function greet executed in 2.0008 seconds

详细说明

  1. 日志配置:我们使用 logging.basicConfig 配置日志记录。日志级别设置为 INFO,日志消息格式包括时间戳、记录器名称、日志级别和消息内容。日志文件名为 performance_log.txt,以追加模式打开。
  2. 装饰器函数:time_decorator 接受一个函数或方法作为参数,并返回一个包装函数 wrapper。在 wrapper 中,记录函数或方法的开始时间和结束时间,并计算执行时间。
  3. 函数示例:我们定义了一个简单的函数 say_hello,并使用装饰器 time_decorator 为其添加执行时间测量功能。调用 say_hello("Alice") 会在控制台输出问候信息,并在 performance_log.txt 文件中记录函数执行的时间。
  4. 类示例:我们定义了一个类 Greeter,其方法 greet 使用装饰器 time_decorator 添加执行时间测量功能。调用 greeter.greet("Bob") 会在控制台输出问候信息,并在 performance_log.txt 文件中记录方法执行的时间。

通过以上两个示例,展示了如何使用 timelogging 模块和装饰器模式来测量并记录函数和方法的执行时间,并将这些性能数据保存到 txt 文件中。这种方法提高了代码的可维护性和功能扩展性,并且能够有效地进行性能分析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ょ镜花う水月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值