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 ...