河北省建設(shè)機(jī)械協(xié)會是真的網(wǎng)站嗎聊城seo
目錄
- with語句
- 上下文管理器原理
- 自定義上下文管理器
- contextmanager 裝飾器
with語句
在我們?nèi)粘J褂脠鼍爸?#xff0c;經(jīng)常會操作一些資源,比如文件對象、數(shù)據(jù)庫連接、Socket連接等,資源操作完了之后,不管操作的成功與否,最重要的事情就是關(guān)閉該資源,否則資源打開太多而沒有關(guān)閉,程序會報(bào)錯(cuò),以文件操作為例,通常我們會這樣寫:
f = open('file.txt', 'w')
try:f.write("Hello")
finally:f.close()
但既然close方法是必須的操作,那就沒必要顯式地調(diào)用,所以Python給我們提供了一種更優(yōu)雅的方式,使用with語句:
with open('file.txt', 'w') as f:f.write("Hello")
在退出with語句下的代碼塊之后,f 對象會自動(dòng)執(zhí)行自己的close方法,實(shí)現(xiàn)資源的釋放,簡潔優(yōu)雅。
上下文管理器原理
上下文管理器實(shí)際是內(nèi)部實(shí)現(xiàn)了__enter__
和__exit__
方法的對象。
當(dāng)我們使用with語法時(shí):
__enter__()
方法:返回一個(gè)值,可以將它賦值給as后面的對象,例如上面的中的f;
__exit__()
方法:with語句退出或者發(fā)送異常時(shí)會執(zhí)行這個(gè)方法。
1、__enter__
方法說明
上下文管理器的__enter__
方法是可以帶返回值的,默認(rèn)返回None,這個(gè)返回值通過with…as…中的 as 賦給它后面的那個(gè)變量,所以 with EXPR as VAR 就是將EXPR對象__enter__
方法的返回值賦給 VAR。
當(dāng)然with...as...
并非固定組合,單獨(dú)使用with...
也是可以的,上下文管理器的__enter__
方法還是正常執(zhí)行,只是這個(gè)返回值并沒有賦給一個(gè)變量,with下面的代碼塊也不能使用這個(gè)返回值。
2、__exit__
方法說明
上下文管理器的__exit__
方法接收3個(gè)參數(shù)exc_type
、exc_val
、exc_tb
,如果代碼塊BLOCK發(fā)生了異常e并退出,這3個(gè)參數(shù)分別為type(e)
、str(e)
、e.__traceback__
,否則都為None。
同樣__exit__
方法也是可以帶返回值的,這個(gè)返回值應(yīng)該是一個(gè)布爾類型True或False,默認(rèn)為None(即False)。如果為False,異常會被拋出,用戶需要進(jìn)行異常處理。如果為True,則表示忽略該異常。
一個(gè)上下文管理器一般使用如下:
with EXPR as VAR:BLOCK
上述代碼的執(zhí)行過程等價(jià)于:
ContextManager = EXPR
VAR = ContextManager.__enter__()
try:BLOCK
finally:ContextManager.__exit__()
f 對象就是把自己的close方法定義在了它的__exit__
方法內(nèi)部,實(shí)現(xiàn)了代碼塊BLOCK執(zhí)行完之后自動(dòng)關(guān)閉自身。
自定義上下文管理器
下面我們定義一個(gè)文件類,內(nèi)部實(shí)現(xiàn)了__enter__
和__exit__
兩個(gè)方法:
class File:def __init__(self, filename, mode):self.filename = filenameself.mode = modedef __enter__(self):print("進(jìn)入")self.f = open(self.filename, self.mode)return self.fdef __exit__(self, exc_type=None, exc_val=None, exc_tbs=None):print("退出")self.f.close()
這時(shí)候File類就是一個(gè)上下文管理器
我們分別通過 with語句 和 try/finally語句 使用File類對文件進(jìn)行寫入操作
通過with語句執(zhí)行:
with File('file.txt', 'w') as f:print("正在寫入...")f.write('Hello')
控制臺輸出:
進(jìn)入
正在寫入...
退出
并得到了一個(gè)寫了 Hello 的 file.txt 文件
通過try/finally語句執(zhí)行:
ContextManager = File('file.txt', 'w')
VAR = ContextManager .__enter__()
try:print("正在寫入...")VAR.write('Hello')
finally:ContextManager.__exit__()
控制臺輸出:
進(jìn)入
正在寫入...
退出
并得到了一個(gè)寫了 Hello 的 file.txt 文件
兩者輸出一致,所以驗(yàn)證了二中執(zhí)行過程的等價(jià)關(guān)系是正確的。
contextmanager 裝飾器
Python還提供了一個(gè)contextmanager裝飾器,允許用戶將一個(gè)生成器定義為上下文管理器,該裝飾器將生成器中的代碼通過yield語句分成兩部分,yield之前的代碼為__enter__
方法,yield之后的代碼為__exit__
方法,yield的返回值即__enter__
方法的返回值,用于賦給as后的變量。
下面我們通過contextmanager裝飾器也實(shí)現(xiàn)一個(gè)關(guān)于文件的上下文管理器:
from contextlib import contextmanager@contextmanager
def open_file(filename, mode):print('進(jìn)入')f = open(filename, mode)try:yield ffinally:print('退出')f.close()
說明:這里使用 try/finally 是確保yield的過程中就算出現(xiàn)異常,文件也能正常關(guān)閉,當(dāng)然這里也能處理異常,使用 try/except/finally 即可。
通過with語句執(zhí)行:
with open_file('file.txt', 'w') as f:print("正在寫入...")f.write('Hello')
執(zhí)行結(jié)果跟之前的上下文管理器執(zhí)行結(jié)果一致,說明contextmanager裝飾器也能定義一個(gè)上下文管理器。
參考:
https://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA==&mid=2653557641&idx=2&sn=fb26d2f9a4b3e0ec707e7da19aee9a29&chksm=806e3d34b719b422fedf9feffa136ddef2d638b59c80921764d60d22b283bc86aae93bfc1c7d&scene=27