应使用 time.perf_counter() 记录函数真实耗时,入口获取 start,出口获取 end,差值即为高精度耗时(秒,保留6位小数);避免日志打爆需加阈值(如 threshold_ms=100)和抽样(如 sample_rate=0.01)。

装饰器里怎么记录函数真实耗时
生产环境要监控执行时间,不能只用 time.time() 粗略包一层——它受系统时钟调整、线程调度影响,误差可能达毫秒级。应该用 time.perf_counter(),它是单调递增的高精度计时器,不受系统时间跳变干扰。
- 在装饰器入口调用一次
start = time.perf_counter() - 函数执行完立刻再调用
end = time.perf_counter() - 耗时就是
end - start,单位是秒,保留6位小数足够定位慢调用 - 别用
datetime.now()或time.clock()(Python 3.8+ 已移除)
如何避免日志打爆磁盘或影响主逻辑
直接每调用都写日志,在高频服务里会拖慢性能甚至填满磁盘。得加控制:只对超阈值、或抽样、或特定函数记录。
- 通过参数传入
threshold_ms=100,只记录超过100ms的调用 - 加
sample_rate=0.01(1%抽样),用random.random() 判断是否记录 - 把日志发到结构化通道(如
logging.getLogger("perf")),方便后续用ELK或Prometheus采集 - 绝对不要在装饰器里做阻塞操作(比如同步写文件、HTTP上报),要用异步队列或延迟提交
装饰器怎么兼容带参数和不带参数的函数
如果装饰器本身要支持配置(比如自定义阈值),就必须能处理 @monitor(threshold_ms=50) 和 @monitor 两种写法,否则会报 TypeError: 'function' object is not callable。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 核心是判断被装饰对象是不是函数:用
inspect.isfunction(func)或检查func is None - 推荐三层嵌套写法:外层接收配置 → 中层返回装饰器 → 内层是实际 wrapper
- 别漏掉
@functools.wraps(func),否则原函数的__name__、__doc__全丢,调试时找不到是谁慢 - 示例关键片段:
def monitor(threshold_ms=100, sample_rate=1.0): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): # ... 计时逻辑 return result return wrapper return decorator if callable(threshold_ms) else decorator
为什么不能在类方法上直接套这个装饰器
类方法第一个参数是 self 或 cls,但普通装饰器不知道上下文,容易把实例当参数传错,导致 TypeError: xxx() missing 1 required positional argument: 'self'。
- 要么显式支持:在
wrapper里判断args and hasattr(args[0], '__dict__'),但不可靠 - 更稳妥的是用
functools.partial或重写为描述符(descriptor),但复杂度高 - 生产建议:统一用
classmethod/staticmethod包一层再装饰;或者改用 AOP 框架如aspectlib - 另一个坑:装饰器在类定义时就执行,如果函数引用了尚未定义的类属性,会触发
NameError
实际部署时,最常被忽略的是计时范围——有人把数据库连接、缓存初始化也包进去了,结果误判业务逻辑慢。监控粒度必须对齐业务语义,该拆就得拆。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










