一、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ì)這樣寫:
但既然close方法是必須的操作,那就沒必要顯式地調(diào)用,所以Python給我們提供了一種更優(yōu)雅的方式,使用with語句:
在退出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)了enter和exit方法的對(duì)象(比如下面EXPR表達(dá)式獲取到的對(duì)象)
它的一般使用方法是:
上述代碼的執(zhí)行過程等價(jià)于:
f 對(duì)象就是把自己的close方法定義在了它的exit方法內(nèi)部,實(shí)現(xiàn)了代碼塊BLOCK執(zhí)行完之后自動(dòng)關(guān)閉自身。
三、自定義上下文管理器
為了驗(yàn)證上述的執(zhí)行過程,我們定義一個(gè)文件類,內(nèi)部實(shí)現(xiàn)了enter和exit兩個(gè)方法:
這時(shí)候File類就是一個(gè)上下文管理器
我們分別通過 with語句 和 try/finally語句 使用File類對(duì)文件進(jìn)行寫入操作
通過with語句執(zhí)行:
控制臺(tái)輸出:
并得到了一個(gè)寫了 Hello 的 file.txt 文件
通過try/finally語句執(zhí)行:
控制臺(tái)輸出:
并得到了一個(gè)寫了 Hello 的 file.txt 文件
兩者輸出一致,所以驗(yàn)證了二中執(zhí)行過程的等價(jià)關(guān)系是正確的
四、enter和exit方法說明
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)于文件的上下文管理器:
說明:這里使用 try/finally 是確保yield的過程中就算出現(xiàn)異常,文件也能正常關(guān)閉,當(dāng)然這里也能處理異常,使用 try/except/finally 即可。
通過with語句執(zhí)行:
執(zhí)行結(jié)果跟之前的上下文管理器執(zhí)行結(jié)果一致,說明contextmanager裝飾器也能定義一個(gè)上下文管理器。
之前寫過一篇文章闡述了裝飾器的原理,現(xiàn)在也知道了上下文管理器就是內(nèi)部實(shí)現(xiàn)了enter和exit方法的對(duì)象,所以有興趣的同學(xué)就可以去看看contextmanager裝飾器的源碼了,看看它是怎么把一個(gè)生成器變成上下文管理器的,其實(shí)稍微想想就知道,肯定是contextmanager裝飾器把生成器加工成一個(gè)內(nèi)部實(shí)現(xiàn)了enter和exit方法的對(duì)象,有時(shí)間的話另寫一篇文章帶大家看看contextmanager的源碼。
六、總結(jié)
- with語句是try/finally語句的替代,簡(jiǎn)化了資源調(diào)用之后的清理工作
- with語句的操作對(duì)象是上下文管理器,它是內(nèi)部實(shí)現(xiàn)了enter和exit方法的對(duì)象
- enter方法可以帶返回值,它通過with…as…語句中的as賦給它后面的變量;exit可返回布爾值,如果為False,異常會(huì)被拋出,用戶需要進(jìn)行異常處理,如果為True,則表示忽略該異常
- Python還提供了contextmanager裝飾器,進(jìn)一步簡(jiǎn)化了上下文管理器的定義方法,它通過把生成器加工成一個(gè)內(nèi)部實(shí)現(xiàn)了enter__
和__exit方法的對(duì)象,從而實(shí)現(xiàn)生成器變上下文管理器