python中的with语句使用于对资源进行访问的场合,保证不管处理过程中是否发生错误或者异常都会执行规定的__exit__(“清理”)操作,释放被访问的资源,比如有文件读写后自动关闭、线程中锁的自动获取和释放等。
与python中with语句有关的概念有:上下文管理协议、上下文管理器、运行时上下文、上下文表达式、处理资源的代码段。
with语句的语法格式如下:
with context_expression [as target(s)]:
with-body
with语句的执行原理:
context_manager=context_expression exit=type(context_manager).__exit__ value=type(context_manager).__enter__(context_manager) exc=True try: try: targer=value with-body except: exc=False if not exit(context_manager, *sys.exc_info()): raise finally: if exc: exit(context_manager,None,None,None)
从with的执行原理中可以看出,context_expression返回的对象中必须具有__exit__和__enter__两个方法,而且先检查__exit__方法在检查__enter__方法。无论在执行with-body中的函数体的过程中是否会发生异常,都会执行__exit__函数,如果没有异常,退出函数的中参数都为None;如果发生异常,则使用sys.exc_info()返回的异常信息为参数退出,并且当__exit__方法返回True时,忽略异常,当__exit__方法返回False时,抛出异常,此时,__exit__函数中就不需要再抛出异常,只需要将__exit__函数的返回值设为False就可以了。
例子:
自定义支持上下文管理协议的类:
不抛出异常的函数体:
抛出异常的函数体:
python提供了contextlib模块,省去了写__enter__和__exit__重复工作了。contextlib模块提供了3个对象:contextmanager装饰器、上下文管理器closing和nested函数。
装饰器contextmanager用来对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器。
yield语句之前的代码在__enter__函数中执行,yield语句之后的代码在__exit__函数中执行。
closing上下文管理器包装起来的对象必须提供close()方法。
closing的执行原理如下:
class closing(object): def __init__(self,thing): self.thing = thing def __enter__(self): return self.thing def __exit__(self, *exc_info): self.thing.close()
因此,closing上下文管理器包装的对象必须提供close()函数。
nested函数可以将多个上下文管理器组织在一起,避免使用嵌套的with语句。
with nested(A(),B(),C()) as (X,Y,Z):
with-body
类似于:
with A() as X:
with B() as Y:
with C() as Z:
with-body