Python 的 with 语句详解
一、简介
with是从Python2.5引入的一个新的语法,更准确的说,是一种上下文的管理协议,用于简化try…except…finally的处理流程。with通过__enter__方法初始化,然后在__exit__中做善后以及处理异常。对于一些需要预先设置,事后要清理的一些任务,with提供了一种非常方便的表达。
with的基本语法如下,EXPR是一个任意表达式,VAR是一个单一的变量(可以是tuple),”asVAR”是可选的。
withEXPRasVAR: BLOCK
根据PEP343的解释,with…as…会被翻译成以下语句:
mgr=(EXPR) exit=type(mgr).__exit__ #Notcallingityet value=type(mgr).__enter__(mgr) exc=True try: try: VAR=value #Onlyif"asVAR"ispresent BLOCK except: #Theexceptionalcaseishandledhere exc=False ifnotexit(mgr,*sys.exc_info()): raise #Theexceptionisswallowedifexit()returnstrue finally: #Thenormalandnon-local-gotocasesarehandledhere ifexc: exit(mgr,None,None,None)
为什么这么复杂呢?注意finally中的代码,需要BLOCK被执行后才会执行finally的清理工作,因为当EXPR执行时抛出异常,访问mgr.exit执行就会报AttributeError的错误。
二、实现方式
根据前面对with的翻译可以看到,被with求值的对象必须有一个__enter__方法和一个__exit__方法。稍微看一个文件读取的例子吧,注意在这里我们要解决2个问题:文件读取异常,读取完毕后关闭文件句柄。用try…except一般会这样写:
f=open('/tmp/tmp.txt') try: forlineinf.readlines(): print(line) finally: f.close()
注意我们这里没有处理文件打开失败的IOError,上面的写法可以正常工作,但是对于每个打开的文件,我们都要手动关闭文件句柄。如果要使用with来实现上述功能,需要需要一个代理类:
classopened(object):
def__init__(self,name): self.handle=open(name)
def__enter__(self): returnself.handle
def__exit__(self,type,value,trackback): self.handle.close()
withopened('/tmp/a.txt')asf: forlineinf.readlines(): print(line)