with 語句適用于對資源進行訪問的場合,確保不管使用過程中是否發(fā)生異常都會執(zhí)行必要的“清理”操作,釋放資源。
比如文件使用后自動關(guān)閉、線程中鎖的自動獲取和釋放等。
with open('test.txt', 'r') as f: for line in f: print(line)
運行機制
with VAR = EXPR:
BLOCK
等價于
VAR = EXPR VAR.__enter__() try: BLOCK finally: VAR.__exit__()
VAR對應(yīng)一個上下文管理器(context manager)。
上下文管理器
實現(xiàn)了__enter__()和__exit__()兩個方法的類就是一個上下文管理器。
class MyContextManager: def __enter__(self): print('before block run') def __exit__(self, exc_type, exc_val, exc_tb): print('after block run')
使用上下文管理MyContextManager
with MyContextManager(): print('run block')
輸出如下:
before block run
run block
after block run
MyContextManager也可以接受參數(shù)
class MyContextManager: def __init__(self, name, age): self.name = name self.age = age def __enter__(self): print('before block run') return self def __exit__(self, exc_type, exc_val, exc_tb): print('after block run') def show(self): print('my name is:', self.name) print('my age is:', self.age)
再次使用上下文管理MyContextManager
with MyContextManager('logan', 27) as myCM: myCM.show()
輸出如下:
before block run
my name is: logan
my age is: 27
after block run
這里用到了一個as:?with context_manager as target
target對應(yīng)context_manager的__enter__()方法的返回值,返回值可以是context_manager自身,也可以是其他對象。
contextlib.contextmanager
?contextmanager可以對生成器函數(shù)進行裝飾,返回結(jié)果是一個上下文管理器。
import contextlib @contextlib.contextmanager def MyGenerator(): print('before yield') yield print('after yield') with MyGenerator(): print('run block')
輸出如下:
before yield
run block
after yield
這里yield nothing,下面給個yield something的例子
import contextlib @contextlib.contextmanager def MyGenerator(): print('before yield') yield 'context manager' print('after yield') with MyGenerator() as cm: print('run block') print(cm)
輸出如下:
before yield
run block
context manager
after yield
?
兩個注意點
1. yield之前的語句類似于__enter__(),yield之后的語句類似于__exit__()方法。
2. yield返回一個上下文管理器,如果使用as語句,會被賦值給as語句中的target。
?
try...finally
當在try范圍內(nèi)產(chǎn)生一個異常時,會立即跳轉(zhuǎn)到finally語句塊。當finally語句塊執(zhí)行完畢后,會繼續(xù)向上一層引發(fā)異常。
?
參考資料:
?