能直接给类方法加装饰器自动计时,关键在于装饰器需适配self/cls参数、用time.perf_counter()精确计时、通过@wraps保留元信息,且逻辑必须置于wrapper内而非装饰器顶层。

直接给类方法加装饰器就能自动计时,关键不是“能不能”,而是“怎么加才不破坏原有逻辑、不影响实例状态、还能准确反映真实耗时”。核心在于装饰器要适配 self 或 cls 参数,且不能干扰方法调用链。
基础版:支持实例方法和类方法的通用计时装饰器
用 *args, **kwargs 接收任意参数,配合 time.perf_counter() 精确计时,再通过 @functools.wraps 保留原方法名和文档:
- 装饰器内部必须正确传递
self(实例方法)或cls(类方法),不能漏掉第一个隐式参数 - 别用
time.time()——它受系统时钟调整影响,perf_counter()才是测量代码执行时间的推荐方式 - 示例写法:
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 耗时: {end - start:.4f}s")
return result
return wrapper
进阶版:区分实例/类/静态方法并保留调用上下文
静态方法没有 self 或 cls,但装饰器本身无法自动识别方法类型。稳妥做法是:统一用通用签名,靠实际调用时传入的参数自然适配——也就是上面的基础版其实已足够健壮。
- 真正要注意的是:不要在装饰器里做
isinstance(args[0], YourClass)这类判断,容易出错且耦合过重 - 如果想按方法类型定制输出(比如标出“[实例]”或“[类]”),可在方法内部用
inspect.ismethod(func)辅助判断,但一般没必要 - 常见误操作:把装饰器写成只接收
self, *args,结果套在类方法上就报错——记住,装饰器封装的是“被调用时的实际参数”,不是定义时的形参名
实用增强:支持阈值提醒与返回耗时值
纯打印不够灵活。可让装饰器在超时时发出提示,或把耗时作为元数据附在返回值上(比如返回 (result, elapsed)):
- 加
warn_threshold=1.0参数,超过该秒数就warnings.warn()提醒 - 加
return_time=False开关,设为True时返回元组,方便后续断言或日志聚合 - 若需记录到日志而非 print,把
print(...)换成logging.info(...)即可,无需改调用方式
避坑提醒:类内装饰的两个典型问题
一是装饰器在类定义阶段就运行,但此时 self 尚未创建——所以所有计时逻辑必须放在 wrapper 内部,而非装饰器函数体顶层;二是多重装饰器顺序错误,比如 @staticmethod 必须在 @timer 外层,否则 self 会丢失。
- ✅ 正确:
@staticmethod<br> @timer<br> def my_func(): ...
- ❌ 错误:
@timer<br> @staticmethod<br> def my_func(): ...
(此时 timer 收到的是未绑定的函数,无 self/cls)

















