首页 > 其他学习 > python > 上下文管理的with语句和contextlib模块
2017
09-29

上下文管理的with语句和contextlib模块

上下文管理:

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

上下文管理器(ContextManager):上下文管理器定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。通常使用with语句调用上下文管理器,也可以通过直接调用其方法来使用。

运行时上下文(runtimecontext):由上下文管理器创建,通过上下文管理器的__enter__()和__exit__()方法实现,__enter__()方法在语句体执行之前进入运行时上下文,__exit__()在语句体执行完后从运行时上下文退出。with语句支持运行时上下文这一概念。

上下文表达式(ContextExpression):with语句中跟在关键字with之后的表达式,该表达式要返回一个上下文管理器对象。

语句体(with-body):with语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的__enter__()方法,执行完语句体之后会执行__exit__()方法。

Python对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于with语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。系统自带默认支持上下文管理协议的对象:

file

decimal.Context

thread.LockType

threading.Lock

threading.RLock

threading.Condition

threading.Semaphore

threading.BoundedSemaphore

with 语句

with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。

with语句的语法格式:

with context_expression [as target(s)]:

    with-body

as部分是可选的:如果它不出现,context_expression的返回值简单地被忽略。如果指定了as子句的话,会将上下文管理器的__enter__()方法的返回值赋值给target(s)。target(s)可以是单个变量或者元组(不能是仅仅由“,”分隔的变量列表,必须由“()”括起来)。执行过程:

1、执行context_expression,生成上下文管理器

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

3、执行语句体with-body

4、不管执行过程中是否发生了异常,执行上下文管理器的__exit__()方法,__exit__()方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句break/continue/return,则以None作为参数调用__exit__(None,None,None);如果执行过程中出现异常,则使用sys.exc_info()方法得到的三个异常信息值:类型(异常类)、值(异常实例)和跟踪记录(traceback)为参数调用__exit__(type,value,traceback)

5、出现异常时,如果__exit__()方法返回False,则会重新抛出异常,让with之外的语句逻辑来处理异常,这也是通用做法;如果返回True,则忽略异常,不再对异常进行处理

contextlib模块

contextlib模块提供了3个对象:装饰器 contextmanager、函数nested和上下文管理器closing。使用这些对象,可以对已有的生成器函数或者对象进行包装,加入对上下文管理协议的支持,避免了专门编写上下文管理器来支持with语句。

装饰器contextmanager:用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器。yield关键字前面的语句可看作在__enter__()函数中的操作,yield之后的操作可以看作在__exit__()函数中的操作。yield后面的值为返回值,只能返回一个值,否则会导致异常RuntimeError;

contextmanager 只是省略了__enter__()和__exit__()的编写,但并不负责实现资源的“获取”和“清理”工作;生成器函数中需要实现必要的清理逻辑控制,包括资源访问出现错误时抛出适当的异常。

from contextlib import contextmanager

@contextmanager

def make_context():

print ('enter')

try:yield "ok"

except RuntimeError as err:print ('error'),err

finally:print ('exit')

with make_context() as value:print (value)

enter

ok

exit

函数nested:可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句。

with contextlib.nested(A(), B(), C()) as (X, Y, Z):

with-body

执行过程

with A() as X:

with B() as Y:

with C() as Z:

with-body

需要注意的是,发生异常后,如果某个上下文管理器的__exit__()方法对异常处理返回False,则更外层的上下文管理器不会监测到异常。

上下文管理器closing:file类直接支持上下文管理器,但有些表示打开句柄的对象并不支持,如urllib.urlopen()返回的对象。为了确保关闭句柄,需要使用closing()为它创建一个上下文管理器,在对象中添加close()方法,然后在执行完毕后执行close()方法来执行所需要的资源“清理”工作。

closing上下文管理器包装起来的对象必须提供close()方法的定义,否则执行时会报 AttributeError 错误。

import contextlib

class myclass():

def __init__(self):print '__init__'

def close(self):print 'close()'

with contextlib.closing(myclass()):print 'ok'

__init__

ok

close()

最后编辑:
作者:qingheluo
这个作者貌似有点懒,什么都没有留下。