Python中的with语句
ref:
https://docs.python.org/release/2.6/whatsnew/2.6.html#pep-343-the-with-statement
https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/
https://www.python.org/dev/peps/pep-0343/
摘自文档:
with替代了之前在python里使用try...finally来做清理工作的方法。基本形式如下:
with expression [as variable]:
with-block
当expression执行的时候,返回一个支持context management protocol(有__enter__(), __exit__()方法)的对象
这个对象的__enter__()方法在with-block执行前运行,该方法返回的结果赋给variable(如果variable存在的话)
with-block执行之后,__exit__()方法被调用,在这里可以执行清理工作
If BLOCK raises an exception, the __exit__(type, value, traceback)() is called with the exception details
with语句的运行过程如下:
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
也就是说:
如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,
则以 None 作为参数调用 __exit__(None, None, None) ;
如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 __exit__(exc_type, exc_value, exc_traceback)
出现异常时,如果 __exit__(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理
支持context management protocol的python对象有file object, threading locks variables, localcontext() fucntion in decimal module
在文档里,有一个连接数据库的例子(省略了部分非关键代码):
class DatabaseConnection: # Database interface def cursor(self): "Returns a cursor object and starts a new transaction" def commit(self): "Commits current transaction" def rollback(self): "Rolls back current transaction" def __enter__(self): # Code to start a new transaction cursor = self.cursor() return cursor def __exit__(self, type, value, tb): if tb is None: # No exception, so commit self.commit() else: # Exception occurred, so rollback. self.rollback() # return False
然后就可以这样使用了:
db_connection = DatabaseConnection() with db_connection as cursor: cursor.execute('insert into ...') cursor.execute('delete from ...') # ... more operations ...