
with 语句是通过上下文管理器协议(Context Manager Protocol)进行资源管理,其核心价值是资源的获取与自动释放。
如果不使用上下文管理器,通常我们可以使用异常机制,在try代码段获取资源,使用资源,在 except、finally 代码段中释放资源。这样在过程中各种异常和失败的情况下都能释放资源。
file = open('some_file.txt', 'w')
try:
file.write('Hello, World!')
finally:
file.close() # 确保在任何情况下文件都会被关闭Python引入了with 语句来简化这种模式:
with open('some_file.txt', 'w') as file:
file.write('Hello, World!')
# 一旦离开 with 代码块,文件会自动关闭,无需手动调用 file.close()open() 函数返回的文件对象就是一个上下文管理器。
对象要成为上下文管理器,需要实现两个特殊方法:__enter__() 和 __exit__()。
__enter__():with 代码块之前被调用。as 关键字后面的变量(如果使用了 as)。__exit__(exc_type, exc_val, exc_tb):exc_type: 异常的类型(如 ValueError, TypeError)。exc_val: 异常的实例值。exc_tb: 异常的追踪信息(traceback object)。with 代码块之后(无论是正常结束还是因异常而结束)被调用。__exit__() 返回 True,则表示异常已经被处理,with 语句之后的代码会继续执行,就好像什么都没发生一样。__exit__() 返回 False 或 None(即不返回任何值),异常会继续向上传播。创建上下文管理器使用类的方法import time
class Timer:
def __enter__(self):
self.start_time = time.time()
print("开始计时...")
return self.start_time # 可以返回自身或其他对象,供 with...as 使用
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.time()
elapsed_time = self.end_time - self.start_time
print(f"代码块运行耗时: {elapsed_time:.4f} 秒")
# 返回 False,让任何发生的异常正常抛出
return False
# 使用自定义的上下文管理器
with Timer() as t:
print(f"开始时间:{t}")
# 模拟一些耗时操作
time.sleep(2)
# 如果这里发生异常,计时功能依然会正常工作
# print(1 / 0) # 取消注释测试异常情况
# 输出:
# 开始计时...
# 开始时间:1755850271.0199914
# 代码块运行耗时: 2.0002 秒使用contextmanager利用生成器函数(带yield语句的函数)from contextlib import contextmanager
import time
@contextmanager
def timer():
start_time = time.time()
print("开始计时...")
try:
yield start_time
# 在这里 yield 之前的部分相当于 __enter__
finally:
# finally 块确保了清理工作一定会执行,相当于 __exit__
end_time = time.time()
elapsed_time = end_time - start_time
print(f"代码块运行耗时: {elapsed_time:.4f} 秒")
# 使用方式完全相同
with timer() as t:
print(f"开始时间:{t}")
time.sleep(1.5)@contextmanager 的工作流程:
yield 之前的代码(相当于 __enter__)。yield 的值会返回给 as 子句。with 块中的代码。with 块中是否发生异常,都会继续执行 yield 之后的代码(在 finally 块中,相当于 __exit__)。async with
async with 是 with 语句的异步版本,用于管理异步资源。
一个对象要成为异步上下文管理器,需要实现两个特殊的异步方法:__aenter__() 和 __aexit__()。
使用装饰器定义上下文管理器则要使用@asynccontextmanager
定义相关函数使用async,调用相关函数使用await。