python中的with语句及上下文管理器

来源:互联网 发布:mac 顿号 编辑:程序博客网 时间:2024/06/05 19:53

with 语句是一种与异常处理相关的功能。with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。

with语句的语法格式如下:

with context_expression [as target(s)]:    with-body

在介绍with语句的工作原理之前,先来了解下上下文管理器。有了上下文管理器,with语句才能工作。

几个术语:

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

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

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

上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。如果指定了 as 子句的话,会将上下文管理器的 __enter__() 方法的返回值赋值给 target(s)。target(s) 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)。

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


Python 的一些内建对象支持上下文管理器,可以用于 with 语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。

with语句可以用try/finally语句替换,如下:

with语句:

with open(r'somefileName') as somefile:    for line in somefile:        print line
try/finally语句:

somefile = open(r'somefileName')try:    for line in somefile:        print linefinally:    somefile.close()

上下文管理器可以在开发中自定义,只需实现__enter__()和__exit__()两个方法。


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

装饰器(contextmanager)

contextmanager 用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器,其 __enter__() 和 __exit__() 方法由 contextmanager 负责提供。

语法:

@contextmanagerdef some_generator(<arguments>):    <setup>    try:        yield <value>    finally:        <cleanup>with some_generator(<arguments>) as <variable>:   <body>

实例:

from contextlib import contextmanager @contextmanagerdef demo():    print '[Allocate resources]'    print 'Code before yield-statement executes in __enter__'    yield '*** contextmanager demo ***'    print 'Code after yield-statement executes in __exit__'    print '[Free resources]' with demo() as value:    print 'Assigned Value: %s' % value输出:
[Allocate resources]
Code before yield-statement executes in __enter__
Assigned Value: *** contextmanager demo ***
Code after yield-statement executes in __exit__
[Free resources]

函数(nested)

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

语法:

with nested(A(), B(), C()) as (X, Y, Z):     # with-body code here

实例:

@contextmanagerdef make_context(name):  print 'entering:', name  yield name  print 'exiting:', namewith make_context('AA') as A, make_context('BB') as B, make_context('CC') as C:#同nested的用法  print 'inside with statement:', A, B, Cwith nested(make_context('AA'), make_context('BB'), make_context('CC')) as (A,B,C):  print 'inside with statement:', A, B, C输出:entering: AAentering: BBentering: CCinside with statement: AA BB CCexiting: CCexiting: BBexiting: AAentering: AAentering: BBentering: CCinside with statement: AA BB CCexiting: CCexiting: BBexiting: AA

上下文管理器(closing)

closing 上下文管理器包装起来的对象必须提供 close() 方法的定义,否则执行时会报 AttributeError 错误。closing 适用于提供了 close() 实现的对象,比如网络连接、数据库连接等,也可以在自定义类时通过接口 close() 来执行所需要的资源“清理”工作。

语法:

with closing(<module>.open(<arguments>)) as f:            <block>

实例:

from contextlib import closing,nested,contextmanagerclass ClosingDemo(object):    def __init__(self):        self.acquire()    def acquire(self):        print 'Acquire resources.'    def free(self):        print 'Clean up any resources acquired.'    def close(self):        self.free() with closing(ClosingDemo()):    print 'Using resources'输出:Acquire resources.Using resourcesClean up any resources acquired.

本文部分摘自https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/,深表感谢。

原创粉丝点击