Python 的 with 语句详解

741次阅读  |  发布于5年以前

一、简介

with是从Python 2.5 引入的一个新的语法,更准确的说,是一种上下文的管理协议,用于简化try…except…finally的处理流程。with通过enter方法初始化,然后在exit中做善后以及处理异常。对于一些需要预先设置,事后要清理的一些任务,with提供了一种非常方便的表达。

with的基本语法如下,EXPR是一个任意表达式,VAR是一个单一的变量(可以是tuple),"as VAR"是可选的。

复制代码 代码如下:

with EXPR as VAR:
BLOCK

根据PEP 343的解释,with…as…会被翻译成以下语句:

复制代码 代码如下:

mgr = (EXPR)
exit = type(mgr).exit # Not calling it yet
value = type(mgr).enter(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:

The exceptional case is handled here

    exc = False  
    if not exit(mgr, *sys.exc_info()):  
        raise  
    # The exception is swallowed if exit() returns true  

finally:

The normal and non-local-goto cases are handled here

if exc:  
    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:
for line in f.readlines():
print(line)
finally:
f.close()

注意我们这里没有处理文件打开失败的IOError,上面的写法可以正常工作,但是对于每个打开的文件,我们都要手动关闭文件句柄。如果要使用with来实现上述功能,需要需要一个代理类:

复制代码 代码如下:

class opened(object):

def __init__(self, name):  
    self.handle = open(name)

def __enter__(self):  
    return self.handle

def __exit__(self, type, value, trackback):  
    self.handle.close()

with opened('/tmp/a.txt') as f:
for line in f.readlines():
print(line)

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8