Python上下文管理器和with塊

python中with語(yǔ)句用起來(lái)很方便,那這后邊的原理就是python中的上下文管理器。

1.什么是上下文管理器

上下文管理器用來(lái)定義/控制代碼塊得執(zhí)行前得準(zhǔn)備工作,以及程序執(zhí)行后得收尾工作。比如文件讀寫前的打開(kāi),與讀寫后的關(guān)閉。 網(wǎng)絡(luò)請(qǐng)求的建立與斷開(kāi)等等。
下邊拿requests.session() 為例:

import requests
with requests.session() as session:
    response_html = session.get('http://www.baidu.com').text
    print (response_html)

為什么with語(yǔ)句塊能控制網(wǎng)絡(luò)的建立與釋放,原因是實(shí)現(xiàn)了上下文管理協(xié)議(context management protocol),相關(guān)的兩個(gè)魔法方法:
__enter__(self): 不接收參數(shù),定義上下文管理器在with語(yǔ)句塊開(kāi)頭所執(zhí)行的操作,其方法返回的值綁定到,/as 后邊所定義的變量。
__exit__(self, exc_type, exc_value, traceback):接收三個(gè)參數(shù),exc_type 表示錯(cuò)誤類型;exc_value 異常實(shí)例。有時(shí)會(huì)有參數(shù)傳給異常構(gòu)造方法,例如錯(cuò)誤消息,這些參數(shù)可以使用exception_type獲取。traceback:traceback對(duì)象

session的上下文協(xié)議源碼


session源碼

2.創(chuàng)建上下文管理器的兩種方式

我們重新實(shí)現(xiàn)一個(gè)類似open()的類,只是在創(chuàng)建文件對(duì)象時(shí)和關(guān)閉文件對(duì)象時(shí)增加了提示。

2.1 實(shí)現(xiàn)上下文協(xié)議
class File(object):
    def __init__(self, file_path, mode):
        self.file_path = file_path
         self.mode = mode

    def __enter__(self):
        self.f = open(self.file_path, self.mode, encoding='utf-8')
        print(f"{self.file_path} is open")
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        print(f"{self.file_path} is closed")


with File('test.txt', 'w') as f:
    f.write('董小賤')

輸出結(jié)果:

test.txt is open
test.txt is closed
2.2 裝飾器實(shí)現(xiàn)
from contextlib import contextmanager

@contextmanager
def file(file_path, mode):
    f = open(file_path, mode, encoding='utf-8')
    print(f"{file_path} is open")

    yield f
    f.close()
    print(f"{file_path} is closed")

with file('test.txt, 'w') as f:
    f.write('董小賤')

輸出結(jié)果:

test.txt is open
test.txt is closed

在使用 @contextmanager 裝飾的生成器中,yield 語(yǔ)句的作用是把函數(shù)的定義體分成兩部 分:yield 語(yǔ)句前面的所有代碼在 with 塊開(kāi)始時(shí)(即解釋器調(diào)用 __enter__方法時(shí))執(zhí)行, yield 語(yǔ)句后面的代碼在 with 塊結(jié)束時(shí)(即調(diào)用 __exit__ 方法時(shí))執(zhí)行。

其實(shí)這里有個(gè)問(wèn)題,如果在with塊中拋出了一個(gè)異常,Python 解釋器會(huì)將其捕獲, 然后在 file 函數(shù)的 yield 表達(dá)式里再次拋出,但是代碼中沒(méi)有處理異常的代碼,所以函數(shù)會(huì)終止,導(dǎo)致文件不能被關(guān)閉。所以我們要在yield處處理異常。

所以第二版:

from contextlib import contextmanager

@contextmanager
def file(file_path, mode):
    f = open(file_path, mode, encoding='utf-8')
    print(f"{file_path} is open")
    try:
        yield f
    except Exception as e:
        print (e) # 處理異常的操作
    finally:
        f.close()
        print(f"{file_path} is closed")
with file('24.txt', 'w') as f:
    f.write('董小賤')

注:實(shí)現(xiàn)上下文管理協(xié)議,即可使用with塊。需要注意的是在 @contextmanager 裝飾器裝飾的生成器中,yield 與迭代沒(méi)有任何關(guān)系。

上下文管理器一個(gè)很好的例子 原地文件讀寫管理器

更多細(xì)節(jié),請(qǐng)參考 《流暢的python》的第十五章節(jié)。

最后編輯于
?著作權(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ù)。

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