with语句会设置一个临时的上下文,交给上下文管理器对象控制,并且负责清理上下问题。
这样做能避免错误并减少样板代码,因此API能更安全,更易使用。除了自动关闭文件之外,with块还有很多用途。
上下文管理器和with块
上下文管理器对象目的是管理with语句,就像迭代器的存在是为了管理for语句一样。
with语句的目的是简化try/finnally模式。
这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码是由于异常、return语句、sys.exit()调用而终止的,也都会执行指定的finally操作。finally子句中通常存放用于释放重要资源,或者还原临时变更的状态。
上下文管理器协议包含__enter__和__exit__两个方法。
with语句开始运行时,会在上下文管理对象上调用__enter__方法;with语句运行结束之后,会在上下文管理对象上调用__exit__方法,以扮演finally子句的角色。
示例,把文件对象当做上下文管理器对象使用。
with open('cafe.txt') as fp:
src = fp.read(60)
print(fp) # fp变量依旧可以用
print(fp.closed, fp.encoding) # 读取fp对象的属性
print(fp.read()) # 但是执行fp的IO操作会异常
打印
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
True cp936
Traceback (most recent call last):
File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 8, in <module>
print(fp.read())
ValueError: I/O operation on closed file.
知识点:
fp变量在上下文管理器之外,依旧存在,可以读取fp对象属性。因为with块于函数和模块不同,没有定义新的作用域。
但是 不能在fp上再执行IO操作,因为在with块的末尾,已经调用了TextIOWrapper__exit__方法把文件关闭了。
执行with后面的表达式的结果是上下文管理器对象,不过,把值绑定到目标变量(as后的变量)是在上下文管理器对象上调用__enter__方法的结果。
不管控制流程以哪种方式退出with块,都会在上下文管理器对象上调用__exit__方法,而不是在__enter__方法返回的对象上调用。
with语句的as子句是可选的。对于像open这样的函数来说,必须加上as子句,以便获取文件的对