您的位置:首页 > 编程语言 > Python开发

python的with语句浅析

2014-09-11 15:54 232 查看
with 语句适用于对资源进行访问的场合,确保不管在使用过程中是否发生了异常,都会进行必要的清理操作,释放资源。例如,文件使用后自动关闭、线程锁的获取和释放等。

与with有关的术语

要使用with语句,首先要明白上下文管理器这一概念。有了上下文管理器,with语句才能工作。下面是有关with语句的一下术语:

上下文管理协议:包含方法__enter__()和__exit__(),该协议的对象要实现这个两个方法。

上下文管理器:支持上下文管理协议的对象,这种对象实现了__enter__()和__exit__()方法。上下文管理器定义了with语句执行前和执行后的操作。通常使用with语句调用上下文管理器,也可以通过直接调用其方法来使用。

with的语法格式

with context_expression [as target(s)]:

with_body

context_expression:叫作with上下文表达式,该表达式要返回一个上下文管理器。

as字句:可选的。如果制定了as子句,会将上下文管理器的__enter__()方法的返回值赋给target(s)。target(s)可以是单个变量,或者由“()”括起来的元组。

with_body:叫作with语句体。语句体中不管是否发生异常,语句体结束后都会对资源进行自动的“清理”工作。在执行语句体之前会调用上下文管理器的__enter__()方法,在执行完语句体后,会执行__exit__()方法。

Python对一些内建的对象进行了改进,加入了对上下文管理器的支持,可以用于with语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。例如,要对一个文件进行操作,使用with语句可以有如下代码:

清单1:with语句操作文件

<pre class="python" name="code">def read_file_with(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'r') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

for line in read_file_with(r"C:\Users\liu.chunming\Desktop\test.txt"):
    print line
这里使用了with语句,不管在处理文件的过程中是否发生异常,都能保证with语句结束后关闭打开的文件句柄。
(该代码中还使用了yield来迭代读取文件内容。利用固定长度的缓冲区来不断读取文件内容。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。)
如果使用传统的try/finally方式,则要写类似的代码如下:


清单2:try/finally语句操作文件

def read_file_tryfinally(fpath):
    f=open(fpath,'r')
    BLOCKSIZE=1024
    try:
        while True:
            block=f.read(BLOCKSIZE)
            if block:
                yield block
            else:
                break
    finally:
            f.close()

for line in read_file_tryfinally(r"C:\Users\liu.chunming\Desktop\test.txt"):
    print line

比较起来,使用 with 语句可以减少编码量。已经加入对上下文管理协议支持的还有模块 threading、decimal 等。

清单3. with 语句执行过程

context_manager = context_expression
    exit = type(context_manager).__exit__  
    value = type(context_manager).__enter__(context_manager)
    exc = True   # True 表示正常执行,即便有异常也忽略;False 表示重新抛出异常,需要对异常进行处理
    try:
        try:
            target = value  # 如果使用了 as 子句
            with-body     # 执行 with-body
        except:
            # 执行过程中有异常发生
            exc = False
            # 如果 __exit__ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常
            # 由外层代码对异常进行处理
            if not exit(context_manager, *sys.exc_info()):
                raise
    finally:
        # 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出
        # 或者忽略异常退出
        if exc:
            exit(context_manager, None, None, None) 
        # 缺省返回 None,None 在布尔上下文中看做是 False

1.执行congtext_expression,生成上下文管理器context_manager

2.调用上下文管理器的__enter__()方法;如果使用了as字句,则将__enter__()的返回值赋值给as字句中额target(s)

3.执行with-body语句体。

4.不管with-body语句体执行过程中是否发生异常,都执行上下文管理器的__exit__方法,__exit__方法负责执行一些清理工作,比如释放资源等。如果执行过程中没有发生异常,或者语句体中执行了语句break、continue、return,则以None作为参数调用__exit__(None,None,None);如果执行过程中出现异常,则使用sys.exc_info得到的异常信息作为参数调用__exit__(exc_type,exc_value,exc_traceback)

5.出现异常时,如果__exit__(type,value,traceback)方法返回False,则会重新抛出异常,让with之外的语句逻辑来处理异常,这也是通用的做法;如果返回True,则忽略异常,不再对异常进行处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: