with 語句适用于對資源進行通路的場合,確定不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源。
比如檔案使用後自動關閉、線程中鎖的自動擷取和釋放等。
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對應一個上下文管理器(context manager)。
上下文管理器
實作了__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也可以接受參數
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()
my name is: logan
my age is: 27
這裡用到了一個as: with context_manager as target
target對應context_manager的__enter__()方法的傳回值,傳回值可以是context_manager自身,也可以是其他對象。
contextlib.contextmanager
contextmanager可以對生成器函數進行裝飾,傳回結果是一個上下文管理器。
import contextlib
@contextlib.contextmanager
def MyGenerator():
print('before yield')
yield
print('after yield')
with MyGenerator():
print('run block')
before yield
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)
context manager
兩個注意點
1. yield之前的語句類似于__enter__(),yield之後的語句類似于__exit__()方法。
2. yield傳回一個上下文管理器,如果使用as語句,會被指派給as語句中的target。
try...finally
當在try範圍内産生一個異常時,會立即跳轉到finally語句塊。當finally語句塊執行完畢後,會繼續向上一層引發異常。
參考資料: