理解 Python 关键字 With
With Statement
和 if, for, while 等一样,with 也是 python 的关键字,于 2.5 版本引入。它封装了 try…except…finally 编码范式,提高了易用性。以读取文件为例,采用 try…except…finally 的代码如下:
try:
f = open('a.txt')
lines = f.readlines()
finally:
f.close()
采用 with 的代码如下:
with open('a.txt') as f:
lines = f.readlines()
当 with 语句结束时,它自动调用 f.close() 关闭文件,故其代码更为简洁,除了文件 IO 的操作以外,with 也非常适合锁和信号量的获取和释放场景。但是并非所有的对象都支持 with,我们把支持 with 的对象称为 context manager。
Context Manager
Context manager 是实现了 context manager 协议的对象,该对象需要实现以下两个方法:
- object.__enter__(self)
该方法在 with 语句开始的时候执行,返回一个对象,这个对象多为 context manager 对象自身,也可以为其它类型的对象,并赋值给 as 后面的参数。以 open(‘a.txt’) 为例,with 在执行该语句时,调用了 open(‘a.txt’).__enter__() 方法,该方法返回对应的文件描述符,并赋给 as 后面的参数 f。
- object.__exit__(self, exc_type, exc_value, traceback)
With 语句在结束时会调用该方法,该方法一般多用于清理现场,比如关闭文件,释放锁等。exc_type, exc_value 和 traceback 记录执行过程中的异常,如果没有异常产生,这三个值均为 None;如果有异常产生,我们可以根据需要在该函数中处理异常,该方法可以充当 try…except…finally 的角色。
一个对象只要实现上述两种方法,该对象就可以被 with 执行,例如:
class ContextManager(object):
def __enter__(self):
print "Enter"
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print "Exit"
with ContextManager() as cm:
print "Body"
输出如下:
Enter
Body
Exit
当执行过程中出现异常时,我们可以在 __exit__() 处理异常,如果不希望把异常往上抛,则把返回值置为 True。
class ContextManager(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print exc_type
print exc_value
print exc_traceback
return True
with ContextManager() as cm:
exc = 1 / 0
print "This line code is still excuted because no exception has been raised."
运行结果如下:
<type 'exceptions.ZeroDivisionError'>
integer division or modulo by zero
<traceback object at 0x10358e5f0>
This line code is still excuted because no exception has been raised.