@mylog.timer 装饰器的详细教学。这通常是指用于测量函数执行时间并记录日志的装饰器。
1. 基础版本:简易计时装饰器
python
import time
import functools
def timer(func):
"""基础计时装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"[TIMER] {func.__name__} 执行时间: {end_time - start_time:.4f} 秒")
return result
return wrapper
# 使用方式
@timer
def slow_function():
time.sleep(1)
return "完成"
slow_function()
# 输出: [TIMER] slow_function 执行时间: 1.0023 秒
2. 进阶版本:集成日志系统(推荐)
实际项目中,我们通常需要专业的日志管理:
python
import logging
import time
import functools
from typing import Callable, Any
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
class MyLog:
"""日志工具类,包含计时装饰器"""
def __init__(self, name: str = "MyLog"):
self.logger = logging.getLogger(name)
def timer(self, level: int = logging.INFO):
"""
带日志级别的计时装饰器
用法:
@mylog.timer()
@mylog.timer(logging.DEBUG)
"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
# 记录开始时间
start_time = time.perf_counter()
try:
# 执行函数
result = func(*args, **kwargs)
# 计算耗时
end_time = time.perf_counter()
duration = end_time - start_time
# 获取函数参数信息
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
# 记录日志
self.logger.log(
level,
f"[TIMER] {func.__name__}({signature}) -> {duration:.4f}s"
)
return result
except Exception as e:
# 异常时也记录耗时
end_time = time.perf_counter()
duration = end_time - start_time
self.logger.error(
f"[TIMER] {func.__name__} 执行失败,耗时: {duration:.4f}s, 错误: {e}"
)
raise
return wrapper
return decorator
# 实例化
mylog = MyLog()
# 使用示例
@mylog.timer()
def calculate_sum(n: int):
"""计算 1 到 n 的和"""
return sum(range(1, n + 1))
@mylog.timer(logging.DEBUG)
def fetch_data(url: str):
"""模拟获取数据"""
time.sleep(0.5)
return f"Data from {url}"
# 测试
calculate_sum(1000000)
fetch_data("https://api.example.com")
3. 高级版本:带配置和统计功能
python
import logging
import time
import functools
import statistics
from typing import Dict, List
from collections import defaultdict
class AdvancedMyLog:
"""高级日志类,支持统计多次调用的性能数据"""
_stats: Dict[str, List[float]] = defaultdict(list)
def __init__(self, name: str = "AdvancedLog"):
self.logger = logging.getLogger(name)
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s | %(name)s | %(levelname)s | %(message)s'
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.DEBUG)
def timer(self, log_args: bool = True, alert_threshold: float = None):
"""
高级计时装饰器
Args:
log_args: 是否记录函数参数
alert_threshold: 超时警告阈值(秒)
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
func_name = func.__qualname__
start = time.perf_counter()
result = func(*args, **kwargs)
duration = time.perf_counter() - start
# 保存统计数据
self._stats[func_name].append(duration)
# 构建日志消息
if log_args and (args or kwargs):
args_str = ", ".join(
[repr(a) for a in args[1:]] + # 排除 self/cls
[f"{k}={v!r}" for k, v in kwargs.items()]
)
msg = f"[TIMER] {func_name}({args_str}) 耗时: {duration:.4f}s"
else:
msg = f"[TIMER] {func_name} 耗时: {duration:.4f}s"
# 检查阈值
if alert_threshold and duration > alert_threshold:
self.logger.warning(f"[SLOW] {msg} (超过阈值 {alert_threshold}s)")
else:
self.logger.info(msg)
return result
return wrapper
return decorator
@classmethod
def get_stats(cls, func_name: str = None):
"""获取性能统计报告"""
if func_name:
times = cls._stats.get(func_name, [])
if not times:
return f"{func_name}: 无数据"
return {
"函数": func_name,
"调用次数": len(times),
"平均耗时": statistics.mean(times),
"最大耗时": max(times),
"最小耗时": min(times),
}
return {name: cls.get_stats(name) for name in cls._stats}
# 使用示例
adv_log = AdvancedMyLog()
class DataProcessor:
@adv_log.timer(alert_threshold=1.0) # 超过1秒警告
def process(self, data: list):
time.sleep(0.8) # 模拟处理
return len(data)
processor = DataProcessor()
processor.process([1, 2, 3])
processor.process([4, 5, 6])
# 查看统计
print(AdvancedMyLog.get_stats("DataProcessor.process"))
4. 异步函数支持
现代 Python 常使用 async/await,需要特殊处理:
python
import asyncio
import time
import functools
class AsyncTimerLog:
def __init__(self):
self.logger = logging.getLogger("AsyncLog")
def timer(self, func):
"""支持异步函数的计时装饰器"""
@functools.wraps(func)
async def async_wrapper(*args, **kwargs):
start = time.perf_counter()
result = await func(*args, **kwargs)
duration = time.perf_counter() - start
self.logger.info(f"[ASYNC TIMER] {func.__name__} 耗时: {duration:.4f}s")
return result
@functools.wraps(func)
def sync_wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
duration = time.perf_counter() - start
self.logger.info(f"[SYNC TIMER] {func.__name__} 耗时: {duration:.4f}s")
return result
# 自动判断是否是协程函数
if asyncio.iscoroutinefunction(func):
return async_wrapper
return sync_wrapper
# 使用
mylog = AsyncTimerLog()
@mylog.timer
async def async_fetch():
await asyncio.sleep(1)
return "data"
@mylog.timer
def sync_fetch():
time.sleep(0.5)
return "data"
# 运行
sync_fetch()
asyncio.run(async_fetch())
5. 使用场景与最佳实践
5.1 性能监控
python
# 监控 API 接口响应时间
@mylog.timer(alert_threshold=2.0)
def api_endpoint():
# 数据库查询等操作
pass
5.2 算法优化对比
python
@mylog.timer()
def algorithm_v1(data):
# 旧算法
pass
@mylog.timer()
def algorithm_v2(data):
# 新算法
pass
# 对比两者的执行时间
5.3 上下文管理器(替代方案)
python
import time
from contextlib import contextmanager
@contextmanager
def timer_context(name: str):
start = time.perf_counter()
yield
duration = time.perf_counter() - start
print(f"[BLOCK TIMER] {name}: {duration:.4f}s")
# 使用
with timer_context("代码块 A"):
time.sleep(1)
6. 注意事项
| 注意点 | 说明 |
|---|---|
| functools.wraps | 必须保留原函数的元数据(__name__, __doc__) |
| 异常处理 | 确保即使函数抛出异常也能记录耗时 |
| 性能开销 | 日志本身有开销,生产环境建议设置开关 |
| 线程安全 | 多线程环境下统计数据需要加锁 |
完整使用模板
python
# config.py 或 utils.py
import logging
import time
import functools
class MyLog:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, name="MyApp"):
if not hasattr(self, 'logger'):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.INFO)
if not self.logger.handlers:
handler = logging.StreamHandler()
handler.setFormatter(
logging.Formatter('%(asctime)s - %(message)s')
)
self.logger.addHandler(handler)
def timer(self, level=logging.INFO):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
result = func(*args, **kwargs)
duration = time.perf_counter() - start
self.logger.log(
level,
f"[⏱️ TIMER] {func.__name__} completed in {duration:.4f}s"
)
return result
except Exception as e:
duration = time.perf_counter() - start
self.logger.error(
f"[⏱️ TIMER] {func.__name__} failed after {duration:.4f}s: {e}"
)
raise
return wrapper
return decorator
# 全局实例
mylog = MyLog()
# 业务代码中使用
@mylog.timer()
def your_function():
pass