侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

实现带参数的装饰器用于函数执行时间统计并支持自定义时间单位

2025-12-11 / 0 评论 / 3 阅读

题目

实现带参数的装饰器用于函数执行时间统计并支持自定义时间单位

信息

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

考点

装饰器定义与使用, 带参数装饰器的实现, 时间处理, 函数包装

快速回答

实现要点:

  • 使用两层嵌套函数实现带参装饰器
  • 内层装饰器使用functools.wraps保留元数据
  • 通过time.perf_counter()精确计时
  • 根据单位参数动态转换时间单位
  • 输出格式:函数名 - 执行时间[单位]
## 解析

问题背景

在性能优化场景中,需要测量函数执行时间并支持灵活切换时间单位(秒/毫秒/微秒)。要求实现一个带参数的装饰器@timeit(unit='ms'),满足:

  • 统计被装饰函数的执行时间
  • 支持单位参数:'s'(秒)、'ms'(毫秒)、'us'(微秒)
  • 输出格式:函数名 executed in 时间值[单位]

解决方案

import time
import functools

def timeit(unit='s'):
    """带参数的时间统计装饰器"""
    # 参数验证
    valid_units = {'s', 'ms', 'us'}
    if unit not in valid_units:
        raise ValueError(f"Invalid unit. Use one of {valid_units}")

    # 单位转换因子
    multipliers = {'s': 1, 'ms': 1000, 'us': 1000000}

    def decorator(func):
        @functools.wraps(func)  # 保留函数元数据
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            end = time.perf_counter()

            # 计算并转换时间
            duration = (end - start) * multipliers[unit]
            print(f"{func.__name__} executed in {duration:.6f}{unit}")
            return result
        return wrapper
    return decorator

# 使用示例
@timeit(unit='ms')
def calculate_sum(n):
    return sum(range(n))

calculate_sum(1000000)  # 输出: calculate_sum executed in 25.456789ms

核心原理

  • 三层结构:装饰器工厂(timeit)→ 装饰器(decorator)→ 包装函数(wrapper
  • 闭包特性:内层函数记住外层作用域(unitmultipliers
  • 时间测量time.perf_counter()提供高精度计时(优于time.time()

最佳实践

  • 使用functools.wraps保留函数名、文档等元数据
  • 用字典存储单位转换因子避免分支判断
  • 格式化输出保留6位小数平衡精度与可读性
  • 对装饰器参数进行有效性校验

常见错误

  • 错误1:忘记使用@functools.wraps导致被装饰函数元数据丢失
  • 错误2:在装饰器工厂内直接实现包装逻辑(缺少decorator层)
  • 错误3:使用time.time()导致计时精度不足
  • 错误4:未处理单位参数大小写(应转换为小写或规范输入)

扩展知识

  • 类装饰器:通过实现__call__方法可达到相同效果
  • 异步支持:用async defawait可扩展为异步函数计时器
  • 日志集成:将print替换为logging模块实现生产级日志
  • 性能分析:结合cProfile可进行更深入的性能诊断