侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1825 篇文章
  • 累计收到 0 条评论

实现一个自定义上下文管理器,用于测量代码块的执行时间

2025-12-12 / 0 评论 / 4 阅读

题目

实现一个自定义上下文管理器,用于测量代码块的执行时间

信息

  • 类型:问答
  • 难度:⭐⭐

考点

上下文管理器, 时间测量, with语句, 异常处理

快速回答

实现要点:

  • 使用__enter__记录开始时间
  • 使用__exit__计算时间差并输出
  • 处理异常时保持计时功能
  • 支持自定义时间单位和输出格式
## 解析

1. 核心原理

Python上下文管理器通过__enter____exit__方法实现:

  • __enter__:进入上下文时执行,返回资源对象
  • __exit__:退出上下文时执行,处理清理工作
结合time模块的perf_counter()(高精度计时器)实现时间测量。

2. 代码实现

import time

class Timer:
    def __init__(self, unit='s', fmt='{:.4f}'):
        self.unit = unit  # 时间单位('s','ms')
        self.fmt = fmt    # 输出格式

    def __enter__(self):
        self.start = time.perf_counter()
        return self  # 返回自身以便访问结果

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.perf_counter()
        self.duration = self.end - self.start

        # 单位转换
        if self.unit == 'ms':
            self.duration *= 1000

        # 异常处理:返回False会传播异常,True则抑制
        print(f"Execution time: {self.fmt.format(self.duration)}{self.unit}")
        return False  

# 使用示例
with Timer(unit='ms') as t:
    time.sleep(0.5)
    # 此处可引发异常测试
print(f"外部访问: {t.duration:.2f}ms")

3. 最佳实践

  • 高精度计时:使用perf_counter()而非time(),避免系统时间调整影响
  • 异常安全:在__exit__中处理异常后应返回False(不抑制异常)
  • 资源访问:通过返回self允许外部访问计时结果
  • 灵活性:支持自定义单位和输出格式

4. 常见错误

  • 错误计时:在__init__中初始化时间(应在__enter__
  • 忽略异常__exit__中直接返回True会吞掉异常
  • 精度不足:使用time.time()可能受系统时钟影响

5. 扩展知识

  • 上下文管理器协议__exit__接收三个异常参数(类型/值/回溯)
  • 替代实现:使用@contextmanager装饰器+生成器:
    from contextlib import contextmanager
    
    @contextmanager
    def timer(unit='s'):
        start = time.perf_counter()
        try:
            yield  # 在此处执行代码块
        finally:
            end = time.perf_counter()
            duration = (end - start) * (1000 if unit=='ms' else 1)
            print(f"Time: {duration:.4f}{unit}")
  • 性能分析:复杂场景建议使用cProfile模块