Python 中的 with 语句用于异常处理,封装了 try…except…finally 编码范式,提高了易用性。with 语句使代码更清晰、更具可读性, 它简化了文件流等公共资源的管理。在处理文件对象时使用 with 关键字是一种很好的做法。
一、Python 中的with使用
在python中涉及资源管理的处理时候,我们通常会使用下面这种方式实现;
with open(filepath, 'r') as f:
# your code
因为通常这类操作会涉及到资源的占用以及控制器的锁定,所以我们有必要对异常情况进行处理。当然,你也可以用
try:
pass # 需要错误处理的代码
except:
pass # 当上述代码发生错误时候进行的操作
finally:
pass # 无论前面两者的执行状态如何都进行的操作
当对两种写法进行比较时候我们发现,with的写法更加简洁且需要关心的操作更加集中;通过对源码的分析,发现with处理的对象实现了上下文管理协议(context manage)。
上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
with是一种控制流协议,其执行过程是:
1、调用context_expression,获取上下文管理对象;
2、调用上述对象的__enter__()方法,执行初始化操作,并返回一个值。若通过as子语句进行别名,则将返回值赋值给target(s);其中,初始化操作由实现__enter__()方法的对象自主实现, with仅关注最后的返回值;
3、初始化完成后,执行with包含body中的代码块。
4、在执行代码块完成或对象被销毁时,上下文管理器中的__exit__()会被调用。
当代码块无异常时,exit()函数会执行一些释放资源的操作,例如关闭文件、断开链接等操作。
当代码块中发生异常时,涉及到__exit__()函数中的这几个参数:exc_type, exc_val, exc_tb。可见官方文档:
官方文档链接:
https://docs.python.org/3/reference/datamodel.html#object
二、With的其他用法。
- with 嵌套:可以将多个 with 语句嵌套,即一个 with 语句块中还包含另一个 with 语句块,用法如下:
with open('file1.txt', 'r') as f1:
with open('file2.txt', 'w') as f2:
# do something
- 自定义 with 语句块:可以通过定义一个类实现自己的 with 语句,实现方法是定义一个类并实现 enter() 和 exit() 方法,enter() 方法在 with 语句块开始时被调用,exit() 方法在 with 语句块结束时被调用。用法如下:
class MyContextManager:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'Entering {self.name}')
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f'Exiting {self.name}')
with MyContextManager('context') as cm:
# do something
- with 语句使用多个上下文管理器:在 with 语句中使用多个上下文管理器,这样就可以同时管理多个资源。用法如下:
with open('file1.txt', 'r') as f1, open('file2.txt', 'w') as f2:
# do something
三、关于错误处理
如果with语句内部发生的错误没有被处理或捕获,那么会向上层抛出异常,并被外部的try语句捕获。因此,如果with语句内部出现了问题,并且没有被处理,那么外部的try语句是可以捕获到这个错误的。如果with语句内部的错误被处理或捕获了,那么外部的try语句是无法捕获到这个错误的。