with语句
在工作当中写代码的时候遇到当需要使用python打开文件的时候,程序会提示你使用with语句而是不是open+close,以前自己都未曾关心为啥,这样做有什么优势,现在越发好奇了。
With语句是什么?
最常见的解释就是说当年你需要打开一个文件的时候,如果只是打开不关闭是危险或者不规范的操作,所以必须要有打开也要有关闭,如下代码:
1  | file = open("test.txt")  | 
但是这里有两个问题:
- 一是可能忘记关闭文件句柄;
 - 二是文件读取数据发生异常,没有进行任何处理。
 
所以为了安全起见推荐有以下改进方法:
1  | file = open("test.txt")  | 
虽然更加安全,但是冗长以及理解更困难的问题也随之出现了。而with语法正好可以解决这个问题:
1  | with open("/tmp/foo.txt") as file:  | 
上下文管理器
with语句的使用被成为上下文管理器。当与第一个例子对比时,可以看到,通过使用 with,许多样板代码(boilerplate code)被消掉了。 这就是 with 语句的主要优势,它确保文件会被关闭,而不用关注嵌套代码如何退出。上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。
以下展示关于上下文管理器的实现代码:
基于类
一个上下文管理器的类,最起码要定义 __enter__ 和 __exit__ 方法。 
1  | class File(object):  | 
通过定义 __enter__ 和 __exit__ 方法,可以在with语句里使用它
1  | with File('demo.txt', 'w') as opened_file:  | 
 __exit__ 函数接受三个参数。这些参数对于每个上下文管理器类中的 __exit__ 方法都是必须的。
- with 语句先暂存了 
File类的__exit__方法。 - 然后它调用 File 类的 
__enter__方法。 __enter__方法打开文件并返回给with语句。- 打开的文件句柄被传递给 
opened_file参数。 - 使用 
.write()来写文件。 with语句调用之前暂存的__exit__方法。__exit__方法关闭了文件。
__exit__ 方法的这三个参数:type,value 和 traceback。 在第4步和第6步之间,如果发生异常,Python 会将异常的 type,value 和 traceback 传递给 __exit__ 方法。 它让 __exit__ 方法来决定如何关闭文件以及是否需要其他步骤。在案例中,并没有注意它们。
那如果文件对象抛出一个异常呢?万一尝试访问文件对象的一个不支持的方法。举个例子:
1  | with File('demo.txt', 'w') as opened_file:  | 
来列一下,当异常发生时,with 语句会采取哪些步骤。
- 它把异常的 
type,value和traceback传递给__exit__方法。 - 它让 
__exit__方法来处理异常。 - 如果 
__exit__返回的是True,那么这个异常就被优雅地处理了。 - 如果 
__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。 
案例中,__exit__ 方法返回的是 None (如果没有 return 语句那么方法会返回 None)。因此,with 语句抛出了那个异常。
1  | Traceback (most recent call last):  | 
尝试下在 __exit__ 方法中处理异常:
1  | class File(object):  | 
__exit__ 方法返回了 True,因此没有异常会被 with 语句抛出。










