針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

一、with語句就是簡(jiǎn)潔版的try/finally語句

在我們?nèi)粘J褂脠?chǎng)景中,經(jīng)常會(huì)操作一些資源,比如文件對(duì)象、數(shù)據(jù)庫連接、Socket連接等,資源操作完了之后,不管操作的成功與否,最重要的事情就是關(guān)閉該資源,否則資源打開太多而沒有關(guān)閉,程序會(huì)報(bào)錯(cuò),以文件操作為例,通常我們會(huì)這樣寫:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

但既然close方法是必須的操作,那就沒必要顯式地調(diào)用,所以Python給我們提供了一種更優(yōu)雅的方式,使用with語句:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

在退出with語句下的代碼塊之后,f 對(duì)象會(huì)自動(dòng)執(zhí)行自己的close方法,實(shí)現(xiàn)資源的釋放,簡(jiǎn)潔優(yōu)雅。

私信回復(fù)【基礎(chǔ)】可獲取基礎(chǔ)視頻教程一套

二、上下文管理器原理

f 對(duì)象之所以會(huì)自動(dòng)執(zhí)行自己的close方法,是因?yàn)樗且粋€(gè)上下文管理器,所以我們要先說說什么是上下文管理器。

上下文管理器是內(nèi)部實(shí)現(xiàn)了enterexit方法的對(duì)象(比如下面EXPR表達(dá)式獲取到的對(duì)象)

它的一般使用方法是:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

上述代碼的執(zhí)行過程等價(jià)于:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

f 對(duì)象就是把自己的close方法定義在了它的exit方法內(nèi)部,實(shí)現(xiàn)了代碼塊BLOCK執(zhí)行完之后自動(dòng)關(guān)閉自身。

三、自定義上下文管理器

為了驗(yàn)證上述的執(zhí)行過程,我們定義一個(gè)文件類,內(nèi)部實(shí)現(xiàn)了enterexit兩個(gè)方法:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

這時(shí)候File類就是一個(gè)上下文管理器

我們分別通過 with語句 和 try/finally語句 使用File類對(duì)文件進(jìn)行寫入操作

通過with語句執(zhí)行:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

控制臺(tái)輸出:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

并得到了一個(gè)寫了 Hello 的 file.txt 文件

通過try/finally語句執(zhí)行:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

控制臺(tái)輸出:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

并得到了一個(gè)寫了 Hello 的 file.txt 文件

兩者輸出一致,所以驗(yàn)證了二中執(zhí)行過程的等價(jià)關(guān)系是正確的

四、enterexit方法說明

1、enter方法說明

上下文管理器的enter方法是可以帶返回值的,默認(rèn)返回None,這個(gè)返回值通過with…as…中的 as 賦給它后面的那個(gè)變量,所以 with EXPR as VAR 就是將EXPR對(duì)象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,異常會(huì)被拋出,用戶需要進(jìn)行異常處理。如果為True,則表示忽略該異常。

五、contextmanager 裝飾器

Python還提供了一個(gè)contextmanager裝飾器,允許用戶將一個(gè)生成器定義為上下文管理器,該裝飾器將生成器中的代碼通過yield語句分成兩部分,yield之前的代碼為enter方法,yield之后的代碼為exit方法,yield的返回值即enter方法的返回值,用于賦給as后的變量。

下面我們通過contextmanager裝飾器也實(shí)現(xiàn)一個(gè)關(guān)于文件的上下文管理器:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

說明:這里使用 try/finally 是確保yield的過程中就算出現(xiàn)異常,文件也能正常關(guān)閉,當(dāng)然這里也能處理異常,使用 try/except/finally 即可。

通過with語句執(zhí)行:

針對(duì)Python的with語句與上下文管理器,為你詳細(xì)解剖

執(zhí)行結(jié)果跟之前的上下文管理器執(zhí)行結(jié)果一致,說明contextmanager裝飾器也能定義一個(gè)上下文管理器。

之前寫過一篇文章闡述了裝飾器的原理,現(xiàn)在也知道了上下文管理器就是內(nèi)部實(shí)現(xiàn)了enterexit方法的對(duì)象,所以有興趣的同學(xué)就可以去看看contextmanager裝飾器的源碼了,看看它是怎么把一個(gè)生成器變成上下文管理器的,其實(shí)稍微想想就知道,肯定是contextmanager裝飾器把生成器加工成一個(gè)內(nèi)部實(shí)現(xiàn)了enterexit方法的對(duì)象,有時(shí)間的話另寫一篇文章帶大家看看contextmanager的源碼。

六、總結(jié)

  1. with語句是try/finally語句的替代,簡(jiǎn)化了資源調(diào)用之后的清理工作
  2. with語句的操作對(duì)象是上下文管理器,它是內(nèi)部實(shí)現(xiàn)了enterexit方法的對(duì)象
  3. enter方法可以帶返回值,它通過with…as…語句中的as賦給它后面的變量;exit可返回布爾值,如果為False,異常會(huì)被拋出,用戶需要進(jìn)行異常處理,如果為True,則表示忽略該異常
  4. Python還提供了contextmanager裝飾器,進(jìn)一步簡(jiǎn)化了上下文管理器的定義方法,它通過把生成器加工成一個(gè)內(nèi)部實(shí)現(xiàn)了enter____exit方法的對(duì)象,從而實(shí)現(xiàn)生成器變上下文管理器
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容