Python的上下文管理器

什么是上下文管理器

上下文管理器是一個對象屠橄,它定義了在執(zhí)行 with 語句時要建立的運行時上下文。 上下文管理器處理進入和退出所需運行時上下文以執(zhí)行代碼塊辜羊。 通常使用 with 語句(在 with 語句中描述)蝗肪,但是也可以通過直接調(diào)用它們的方法來使用。

首先我們看下面操作文件的代碼贰谣,理清幾個概念娜搂,不要弄混了

with open("test.txt") as f:
    print(f.readlines())
  • with open("test.txt") as f:上下文表達式
  • open("test.txt"):上下文管理器
  • f:至于f,f不是上下文管理器吱抚,f應(yīng)該是資源對象

上下文管理協(xié)議

  • 在一個類中百宇,如果實現(xiàn)了__enter____exit__這兩個魔法方法,這個類的實例就是一個上下文管理器秘豹。
  • 如果使用了上下文管理器携御,盡管with沒有調(diào)用魔法方法,但是with在代碼塊執(zhí)行前還是會先執(zhí)行__enter__既绕,在代碼執(zhí)行結(jié)束或出錯的時候執(zhí)行__enter__
  • __enter__: with語句中的代碼塊執(zhí)行前執(zhí)行__enter__, 返回的值將賦值給with句中as后的變量.
  • __exit__: with語句中的代碼塊執(zhí)行結(jié)束或出錯, 會執(zhí)行__exit__
class Resource():
    def __enter__(self):
        print('===connect to resource===')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('===close resource connection===')

    def operate(self):
        print('===in operation===')


with Resource() as res:
    res.operate()

"""
輸出

===connect to resource===
===in operation===
===close resource connection===
"""
  • 在編寫代碼時啄刹,我們一般將資源的連接或者獲取放在__enter__中,而將資源的關(guān)閉寫在__exit__ 中凄贩。

為什么要使用上下文管理器

  1. 使用上下文管理器會讓代碼看起來更簡潔優(yōu)雅誓军,這也是Python一直追求的。我們可以用上下文管理器操作(創(chuàng)建/獲取/釋放)資源怎炊,如文件操作谭企、數(shù)據(jù)庫連接
  2. 也可以用上下文管理器處理異常评肆。我們一般用try...except...來處理異常但是這樣做一個不好的地方是债查,在代碼的主邏輯里,會有大量的異常處理代理瓜挽,這會很大的影響我們的可讀性盹廷。如果用上下文管理器,就可以使用with將異常的處理隱藏起來久橙。也就是說俄占,with大大簡化了try...except..語句的異常處理

舉個栗子,下面的代碼我們將操作1/0 這個錯誤淆衷「组看看是否不報錯。

class Resource():
    def __enter__(self):
        print('===connect to resource===')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('===close resource connection===')
        print(exc_type, exc_val, exc_tb)
        return True

    def operate(self):
        1/0  # 分母不能為0祝拯,這里應(yīng)該報錯


with Resource() as res:
    res.operate()

"""
輸出

===connect to resource===
===close resource connection===
<class 'ZeroDivisionError'> division by zero <traceback object at 0x0000024A3C452E08>
"""

這就是上下文管理協(xié)議的一個強大之處甚带,異乘希可以在__exit__ 進行捕獲并由你自己決定如何處理,是拋出呢還是在這里就解決了鹰贵。在__exit__ 里返回 True(沒有return 就默認為 return False)晴氨,就相當于告訴 Python解釋器,這個異常我們已經(jīng)捕獲了碉输,不需要再往外拋了籽前。

在 寫__exit__ 函數(shù)時,需要注意的事敷钾,它必須要有這三個參數(shù):

  • exc_type:異常類型
  • exc_val:異常值
  • exc_tb:異常的錯誤棧信息

當主邏輯代碼沒有報異常時枝哄,這三個參數(shù)將都為None。

理解并使用裝飾器 contextlib

上面說了闰非,如果要定義上下文管理器膘格,就需要在類中定義__enter____exit__。在Python中也提供了一個@contextlib裝飾器财松,可以省略兩個魔法方法。該裝飾器位于contextlib模塊下

from contextlib import contextmanager
  • 我們借助contextmanager裝飾器纱控,可以不使用兩個魔法方法辆毡。但是這里注意我們只是不需要定義__enter____exit__這兩個方法,但是他們里面所執(zhí)行的語句我們還是需要實現(xiàn)的甜害。在進入上下文管理器的時候打印__enter__里面的方法舶掖,在退出的時候打印__exit__里面的方法。
from contextlib import contextmanager

@contextmanager
def open_func(file_name):
    # __enter__ 方法
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')

    # 【重點】:yield 返回的內(nèi)容復(fù)制給as之后的變量
    yield file_handler

    # __exit__方法
    print('close file:', file_name, 'in __exit__')
    file_handler.close()
    return

with open_func('E:/hello.txt') as f:
    for line in f:
        print(line)
        
"""
輸出

open file: E:/hello.txt in __enter__
1

2

3

close file: E:/hello.txt in __exit__
"""

上面代碼的執(zhí)行過程:

  • with語句中的代碼塊執(zhí)行函數(shù)中yield語句之前的代碼尔店,相當于執(zhí)行__enter__ 方法眨攘。
  • yield返回的內(nèi)容復(fù)制給as之后的變量,也就是f嚣州。 在被裝飾函數(shù)里鲫售,必須是一個生成器(帶有yield)
  • with語句中的代碼塊執(zhí)行函數(shù)中yield語句之后的代碼,相當于執(zhí)行__exit__ 方法该肴。

上下文管理器的三個好處

  1. 提高代碼的復(fù)用率
  2. 提高代碼的優(yōu)雅度
  3. 提高代碼的可讀性
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末情竹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匀哄,更是在濱河造成了極大的恐慌秦效,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涎嚼,死亡現(xiàn)場離奇詭異阱州,居然都是意外死亡,警方通過查閱死者的電腦和手機法梯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門苔货,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事蒲赂≮逡保” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵滥嘴,是天一觀的道長木蹬。 經(jīng)常有香客問我,道長若皱,這世上最難降的妖魔是什么镊叁? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮走触,結(jié)果婚禮上晦譬,老公的妹妹穿的比我還像新娘。我一直安慰自己互广,他們只是感情好敛腌,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惫皱,像睡著了一般像樊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旅敷,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天生棍,我揣著相機與錄音,去河邊找鬼媳谁。 笑死涂滴,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的晴音。 我是一名探鬼主播柔纵,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼段多!你這毒婦竟也來了首量?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤进苍,失蹤者是張志新(化名)和其女友劉穎加缘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體觉啊,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡拣宏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了杠人。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勋乾。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宋下,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辑莫,到底是詐尸還是另有隱情学歧,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布各吨,位于F島的核電站枝笨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏揭蜒。R本人自食惡果不足惜横浑,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屉更。 院中可真熱鬧徙融,春花似錦、人聲如沸瑰谜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萨脑。三九已至脚猾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砚哗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工砰奕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛛芥,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓军援,卻偏偏與公主長得像仅淑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胸哥,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354