Python 中的with关键字使用详解
在 Python 2.5 中, with 关键字被加入。它将常用的 try ... except ... finally ... 模式很方便的被复用。看一个最经典的例子: with open('file.txt') as f: content = f.read() 在这段代码中,无论 with 中的代码块在执行的过程中发生任何情况,文件最终都会被关闭。如果代码块在执行的过程中发生了一个异常,那么在这个异常被抛出前,程序会先将被打开的文件关闭。 再看另外一个例子。 在发起一个数据库事务请求的时候,经常会用类似这样的代码: db.begin() try: # do some actions except: db.rollback() raise finally: db.commit() 如果将发起事务请求的操作变成可以支持 with 关键字的,那么用像这样的代码就可以了: with transaction(db): # do some actions 下面,详细的说明一下 with 的执行过程,并用两种常用的方式实现上面的代码。 with 的一般执行过程 一段基本的 with 表达式,其结构是这样的: with EXPR as VAR: BLOCK 其中: EXPR 可以是任意表达式; as VAR 是可选的。其一般的执行过程是这样的:
将这个过程用代码表示,是这样的: mgr = (EXPR) exit = type(mgr).__exit__ # 这里没有执行 value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # 如果有 as VAR BLOCK except: exc = False if not exit(mgr,*sys.exc_info()): raise finally: if exc: exit(mgr,None,None) 这个过程有几个细节: 如果上下文管理器中没有 __enter()__ 或者 __exit()__ 中的任意一个方法,那么解释器会抛出一个 AttributeError 。 接下来,用两种方法来实现上面来实现上面的过程的吧。 实现上下文管理器类 第一种方法是实现一个类,其含有一个实例属性 db 和上下文管理器所需要的方法 __enter()__ 和 __exit()__ 。 class transaction(object): def __init__(self,db): self.db = db def __enter__(self): self.db.begin() def __exit__(self,type,value,traceback): if type is None: db.commit() else: db.rollback() 了解 with 的执行过程后,这个实现方式是很容易理解的。下面介绍的实现方式,其原理理解起来要复杂很多。 使用生成器装饰器 在Python的标准库中,有一个装饰器可以通过生成器获取上下文管理器。使用生成器装饰器的实现过程如下: from contextlib import contextmanager @contextmanager def transaction(db): db.begin() try: yield db except: db.rollback() raise else: db.commit() 第一眼上看去,这种实现方式更为简单,但是其机制更为复杂。看一下其执行过程吧:
再次看看上述过程的代码大致实现: def contextmanager(func): def helper(*args,**kwargs): return GeneratorContextManager(func(*args,**kwargs)) return helper class GeneratorContextManager(object): def __init__(self,gen): self.gen = gen def __enter__(self): try: return self.gen.next() except StopIteration: raise RuntimeError("generator didn't yield") def __exit__(self,traceback): if type is None: try: self.gen.next() except StopIteration: pass else: raise RuntimeError("generator didn't stop") else: try: self.gen.throw(type,traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration: return True except: if sys.exc_info()[1] is not value: raise 总结 Python的 with 表达式包含了很多Python特性。花点时间吃透 with 是一件非常值得的事情。 一些其他的例子 锁机制 @contextmanager def locked(lock): lock.acquired() try: yield finally: lock.release() 标准输出重定向 @contextmanager def stdout_redirect(new_stdout): old_stdout = sys.stdout sys.stdout = new_stdout try: yield finally: sys.stdout = old_stdout with open("file.txt","w") as f: with stdout_redirect(f): print "hello world" 参考资料 The Python “with” Statement by Example PEP 343 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |