import contextlib
@contextlib.contextmanager
def file_manager(file_path, mode):
try:
file = open(file_path, mode)
yield file
finally:
file.close()
@contextlib.contextmanager
def network_connection_manager():
try:
# 这里模拟网络连接
print("建立网络连接")
yield "模拟网络连接对象"
finally:
print("关闭网络连接")
@contextlib.contextmanager
def database_connection_manager():
try:
# 这里模拟数据库连接
print("建立数据库连接")
yield "模拟数据库连接对象"
finally:
print("关闭数据库连接")
def main():
file_path = "test.txt"
with file_manager(file_path, 'w') as file, \
network_connection_manager() as network_conn, \
database_connection_manager() as db_conn:
file.write("测试内容")
# 这里模拟可能发生异常的操作
# raise ValueError("模拟异常")
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"捕获到异常: {e}")
异常处理和资源清理原理
- 上下文管理器协议:Python的上下文管理器遵循
__enter__
和__exit__
方法协议。当使用with
语句进入上下文时,会调用__enter__
方法获取资源;当离开上下文时(正常离开或因异常离开),会调用__exit__
方法释放资源。
- 嵌套上下文管理器:在嵌套的
with
语句中,Python会按照从左到右的顺序依次进入每个上下文管理器,调用它们的__enter__
方法获取资源。如果在某个上下文管理器内部发生异常,Python会从最内层的上下文管理器开始,依次调用它们的__exit__
方法来清理资源。
- 异常传递:如果
__exit__
方法返回False
(默认行为),异常会继续向外层传递;如果返回True
,则表示异常已被处理,不再继续传递。在上述代码中,每个上下文管理器的__exit__
方法(由contextlib.contextmanager
装饰器生成)没有显式返回,默认返回False
,所以异常会传递到外层try - except
块中。
- 资源清理:无论是否发生异常,
with
语句结束时都会确保所有进入的上下文管理器的__exit__
方法被调用,从而保证资源的正确释放。在代码中,文件、网络连接和数据库连接在离开with
块时都会被关闭,即使中间发生异常。