使用装饰器计时无需修改函数内部代码,通过在调用前后记录时间差来统计执行耗时;2. 核心实现是利用time.perf_counter()获取高精度时间,结合functools.wraps保留原函数元信息;3. 装饰器的优势在于解耦和复用,避免在多个函数中重复插入计时代码;4. 可扩展为带参数的装饰器,支持自定义日志级别、输出格式等;5. 注意事项包括装饰器自身开销、i/o等待时间影响、递归函数的重复计时问题以及异步函数需使用async装饰器。该方法在不侵入业务逻辑的前提下实现高效性能监控,适用于大多数常规场景的执行时间分析。

说白了,Python里用装饰器来统计函数执行时间,就是给函数套个“壳”,这个“壳”负责在函数跑之前记个时,跑完之后再记个时,然后一减,时间就出来了。核心思想就是不改动函数本身的代码,而是通过一个外部机制来增强它,让你的核心业务逻辑保持干净利落。这对于性能分析、调试,或者仅仅是想知道某个操作到底耗了多久,都非常方便。
要实现一个函数计时装饰器,我们通常会用到Python内置的
time
time.perf_counter()
下面是一个基础的计时装饰器实现:
立即学习“Python免费学习笔记(深入)”;
import time
import functools # 用于保留被装饰函数的元数据
def timer(func):
"""
一个简单的计时装饰器,用于测量函数执行时间。
"""
@functools.wraps(func) # 这一行很重要,它能保留原函数的__name__, __doc__等属性
def wrapper(*args, **kwargs):
start_time = time.perf_counter() # 记录开始时间
result = func(*args, **kwargs) # 执行被装饰的函数
end_time = time.perf_counter() # 记录结束时间
duration = end_time - start_time # 计算执行时长
print(f"函数 '{func.__name__}' 执行耗时: {duration:.4f} 秒")
return result # 返回被装饰函数的执行结果
return wrapper
# 如何使用这个装饰器
@timer
def example_function(n):
"""一个模拟耗时操作的函数"""
print(f"开始执行 example_function({n})...")
time.sleep(n) # 模拟耗时操作
print(f"example_function({n}) 执行完毕。")
return f"完成了 {n} 秒的等待"
@timer
def another_task(a, b):
"""另一个简单的函数"""
print(f"正在计算 {a} + {b}...")
total = sum(range(a * b)) # 一个稍微耗时的计算
return total
if __name__ == "__main__":
print("--- 第一次调用 ---")
example_function(1) # 调用被装饰的函数
print("\n--- 第二次调用 ---")
example_function(0.5)
print("\n--- 第三次调用 ---")
result = another_task(1000, 2000)
print(f"another_task 的结果是: {result}")这段代码里,
timer
func
wrapper
wrapper
func
timer
wrapper
@functools.wraps(func)
wrapper
func
说实话,刚开始学编程的时候,我也会想,直接在函数开头和结尾各加一行计时代码不就得了?比如这样:
import time
def my_old_function():
start = time.perf_counter()
# 核心业务逻辑
time.sleep(1)
end = time.perf_counter()
print(f"耗时: {end - start} 秒")这当然能工作,但想想看,如果你的项目里有几十上百个函数都需要计时呢?你得把这几行代码复制粘贴几十上百次。这简直是维护者的噩梦!
我觉得,用装饰器来做计时,最大的好处就是“解耦”和“复用”。
@timer
timer
这种方式,在我实际的项目经验里,真的是屡试不爽。它让我在不修改原有业务代码的前提下,轻松地添加或移除各种辅助功能,大大提升了开发效率和代码质量。
既然我们已经掌握了基础,那自然会想,这玩意儿还能玩出什么花样来?计时装饰器远不止打印个时间那么简单,它有很多扩展的可能性,能让你的程序分析能力更上一层楼。
带参数的计时装饰器: 有时候你可能想控制计时结果的输出格式,或者决定是否要打印结果。这时候,你可以让装饰器本身接收参数。这需要多一层函数嵌套:
import time
import functools
import logging # 引入日志模块
# 配置一个简单的日志器
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def configurable_timer(log_level=logging.INFO, message_prefix=""):
"""
一个可配置的计时装饰器,可以指定日志级别和消息前缀。
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
duration = end_time - start_time
log_message = f"{message_prefix}函数 '{func.__name__}' 执行耗时: {duration:.4f} 秒"
logging.log(log_level, log_message) # 使用日志模块输出
return result
return wrapper
return decorator
@configurable_timer(log_level=logging.DEBUG, message_prefix="[DEBUG_TIMING] ")
def debug_task():
time.sleep(0.1)
print("这是一个调试任务。")
@configurable_timer(log_level=logging.WARNING, message_prefix="[PERFORMANCE_WARNING] ")
def critical_task():
time.sleep(0.8)
print("这是一个关键任务,可能耗时过长。")
if __name__ == "__main__":
print("\n--- 可配置计时器示例 ---")
debug_task()
critical_task()这样,你就可以根据需要调整装饰器的行为,比如在开发环境打印详细的DEBUG信息,而在生产环境只记录WARNING级别的性能告警。
记录到文件或数据库: 如果你的系统需要长期监控性能,仅仅打印到控制台肯定不够。你可以把计时结果写入日志文件,或者存储到数据库中,方便后续的数据分析和可视化。这只需要在
wrapper
聚合统计: 对于频繁调用的函数,你可能不希望每次都打印一行日志,而是希望在程序运行结束后,能看到这个函数总共被调用了多少次,平均每次耗时多少,最大耗时多少等等。这可以通过在装饰器外部维护一个字典或列表来存储每次调用的数据,然后在程序退出时统一处理。
这些扩展思路,让简单的计时装饰器变得异常强大,能适应各种复杂的性能监控需求。
任何工具用起来,总会有些需要留心的小细节,计时装饰器也不例外。在我用它的过程中,也遇到过一些让我挠头的问题,总结下来,主要有这么几点:
装饰器本身的开销: 虽然
time.perf_counter()
I/O密集型操作的“误导性”:
time.perf_counter()
time.process_time()
递归函数的计时: 如果你直接把计时装饰器应用到一个递归函数上,比如计算斐波那契数列的递归函数:
@timer
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)你会发现,每次递归调用都会触发计时器的打印。这会导致输出信息爆炸,而且你可能只关心最外层那次
fibonacci(n)
异步函数(async def
asyncio
async def
await
import time
import functools
import asyncio
def async_timer(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs): # 注意这里是 async def
start_time = time.perf_counter()
result = await func(*args, **kwargs) # 注意这里是 await
end_time = time.perf_counter()
duration = end_time - start_time
print(f"异步函数 '{func.__name__}' 执行耗时: {duration:.4f} 秒")
return result
return wrapper
@async_timer
async def async_example():
print("开始异步任务...")
await asyncio.sleep(0.2) # 模拟异步I/O等待
print("异步任务完成。")
if __name__ == "__main__":
print("\n--- 异步计时器示例 ---")
asyncio.run(async_example())记住,同步装饰器不能直接用于异步函数,反之亦然。这在我第一次碰到的时候,着实花了一点时间才反应过来。
这些“坑”和注意事项,并不是说装饰器不好用,而是任何工具都有其适用场景和局限性。了解它们,能让你在实际项目中更准确、更高效地利用计时装饰器进行性能分析。
以上就是Python函数怎样用装饰器实现函数执行时间统计 Python函数计时装饰器的入门编写方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号