一篇文章告訴你Python上下文管理器怎么用

一般我們操作文件的時(shí)候憎茂,大家都會(huì)知道我們使用with的方式去寫越除,一般如下

with open('xxxx/test.txt') as f:
'xxxxxx'

那么我們?yōu)槭裁匆褂眠@種方式去操作文件呢焊切?因?yàn)閣ith這種方式自動(dòng)幫我們執(zhí)行了close關(guān)閉文件句柄的操作逻澳,免的我們忘記關(guān)閉句柄,浪費(fèi)資源。

那我們?yōu)槭裁词鞘褂脀ith方式就可以達(dá)到這種效果呢檩禾?這就是我們今天說的python的上下文管理器的作用挂签。簡(jiǎn)單來說上下文管理器必須在這個(gè)對(duì)象的類中聲明enterexit方法

下面我們看看怎么定義自己的上下文管理器:

class Context:
def init(self,name):
self.name=name

# 下面使用with語句, 對(duì)象的__enter__被觸發(fā), 返回值則賦值給as聲明的變量,這里就是f
def __enter__(self):
    print('自定義上下文管理器')
    # return self
#with中代碼塊執(zhí)行完畢時(shí)觸發(fā)執(zhí)行盼产,用來釋放資源饵婆,例如文件句柄,數(shù)據(jù)庫連接等
def __exit__(self, exc_type, exc_val, exc_tb):
    print('程序運(yùn)行結(jié)束戏售,釋放資源')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    # return True

with Context('test.txt') as f:
print('主邏輯過程')

結(jié)果:
自定義上下文管理器
主邏輯過程
程序運(yùn)行結(jié)束侨核,釋放資源
None
None
None

我們可以看看執(zhí)行的順序
with觸發(fā)enter方法
然后執(zhí)行主邏輯過程
所有程序執(zhí)行完之后觸發(fā)exit方法,釋放資源

這就是為什么我們用with方法操作文件時(shí)候不用f.close的原因灌灾,因?yàn)樵?strong>exit方法中幫我們自動(dòng)釋放了文件句柄

exit()中的三個(gè)參數(shù)分別代表異常類型搓译,異常值和追溯信息,with語句中代碼塊出現(xiàn)異常,則with后的代碼都無法執(zhí)行

下面我們?cè)陬愔屑尤氘惓4a塊锋喜,看看什么情況些己,是否with后的代碼都無法執(zhí)行

class Context:
def init(self,name):
self.name=name

# 下面使用with語句, 對(duì)象的__enter__被觸發(fā), 返回值則賦值給as聲明的變量,這里就是f
def __enter__(self):
    print('自定義上下文管理器')
    # return self
#with中代碼塊執(zhí)行完畢時(shí)觸發(fā)執(zhí)行嘿般,用來釋放資源段标,例如文件句柄,數(shù)據(jù)庫連接等
def __exit__(self, exc_type, exc_val, exc_tb):
    print('程序運(yùn)行結(jié)束炉奴,釋放資源')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    # return True
def  get_values(self):
    dic = {'a':1,'b':2}
    print(dic['c']) # 取一個(gè)不存在的key

p = Context('test.txt')
with Context('test.txt') as f:
print('主邏輯過程')
Context.get_values(p)
print('會(huì)不會(huì)執(zhí)行我們') #主邏輯

結(jié)果:

自定義上下文管理器
主邏輯過程
Traceback (most recent call last):
程序運(yùn)行結(jié)束逼庞,釋放資源
File "C:/Users/aryin/Desktop/mysite2/上下文管理器.py", line 24, in <module>
<class 'KeyError'>
Context.get_values(p)
File "C:/Users/aryin/Desktop/mysite2/上下文管理器.py", line 19, in get_values
'c'
print(dic['c']) # 取一個(gè)不存在的key
<traceback object at 0x000001E50A7C04C8>
KeyError: 'c'

可以看到報(bào)出KeyError,這也是我們預(yù)測(cè)到的瞻赶,主邏輯也確實(shí)沒有執(zhí)行(print('會(huì)不會(huì)執(zhí)行我們') 沒有執(zhí)行)赛糟,那我們?cè)趺蠢?strong>exit中的三個(gè)參數(shù)捕獲異常并且實(shí)現(xiàn)代碼繼續(xù)執(zhí)行呢?

class Context:
def init(self,name):
self.name=name

# 下面使用with語句, 對(duì)象的__enter__被觸發(fā), 返回值則賦值給as聲明的變量砸逊,這里就是f
def __enter__(self):
    print('自定義上下文管理器')
    # return self
#with中代碼塊執(zhí)行完畢時(shí)觸發(fā)執(zhí)行虑灰,用來釋放資源,例如文件句柄痹兜,數(shù)據(jù)庫連接等
def __exit__(self, exc_type, exc_val, exc_tb):
    print('程序運(yùn)行結(jié)束穆咐,釋放資源')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    return True  #新添加這句
def  get_values(self):
    dic = {'a':1,'b':2}
    print(dic['c']) # 取一個(gè)不存在的key

p = Context('test.txt')
with Context('test.txt') as f:
print('主邏輯過程')
Context.get_values(p)
print('會(huì)不會(huì)執(zhí)行我們') #主邏輯

結(jié)果:
自定義上下文管理器
主邏輯過程
程序運(yùn)行結(jié)束,釋放資源
<class 'KeyError'>
'c'
<traceback object at 0x00000187D3EC0488>
會(huì)不會(huì)執(zhí)行我們

我們可以看到程序沒有奔潰字旭,我們執(zhí)行在exit函數(shù)中增加了reture True对湃,實(shí)現(xiàn)異常被捕獲,并且主邏輯繼續(xù)執(zhí)行(print('會(huì)不會(huì)執(zhí)行我們'))
遗淳,也就是說如果__exit()返回值為True,那么異常會(huì)被清空拍柒,就好像啥都沒發(fā)生一樣,with后的語句正常執(zhí)行屈暗。

我們可以使用with的上下文管理器更優(yōu)雅的處理代碼的中的異常捕獲拆讯,而不用try..execpt..這種方法去捕獲脂男,并且可以實(shí)現(xiàn),程序執(zhí)行完畢之后种呐,我們想操作的一些資源釋放操作宰翅,這樣增加了代碼的簡(jiǎn)潔程度和可讀性。

下面我們看看一個(gè)文件的寫入的With案例

class Context:
def init(self,filepath,mode='r',encoding='utf-8'):
self.filepath=filepath
self.mode=mode
self.encoding=encoding

def __enter__(self):
    # print('enter')
    self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
    return self.f

def __exit__(self, exc_type, exc_val, exc_tb):
    # print('exit')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    self.f.close()
    return True
def __getattr__(self, item):
    return getattr(self.f,item)

with Context('file_read.txt','w') as f:
print(f)
f.write('testfile') #正常寫入
f.xxxxx #拋出異常爽室,exit處理捕獲異常汁讼,并且釋放文件句柄

結(jié)果:
<_io.TextIOWrapper name='file_read.txt' mode='w' encoding='utf-8'>
<class 'AttributeError'>
'_io.TextIOWrapper' object has no attribute 'wasdf'
<traceback object at 0x0000019C630D0488>

下面介紹另外一種實(shí)現(xiàn)上下文管理器的方式,上面我們通過類的方式去實(shí)現(xiàn)阔墩,下面我們通過方法加裝飾器的方式實(shí)現(xiàn)嘿架,具體的原理上面說過了,這里就不說了啸箫,這種方法也許更加歡迎耸彪。

import contextlib

裝飾器

@contextlib.contextmanager
def context(filepath,mode='r',encoding='utf-8'):

#這段相關(guān)于__enter__方法
print('這是__enter__')
f = open(filepath, mode=mode,encoding=encoding)

try:
    yield f
except Exception as e:  #捕獲異常
    print(e)
finally:

    # 這段相關(guān)于__exit__方法
    print('這是__exit__')
    f.close()

    return True

下面用法相同

with context('file_read.txt','w') as f:
print(f)
f.write('testfile') #正常寫入
f.xxxxx #拋出異常,exit處理捕獲異常忘苛,并且釋放文件句柄

最后我們說說Python的優(yōu)點(diǎn)吧

1.使用with語句的目的就是把代碼塊放入with中執(zhí)行搜囱,with結(jié)束后,自動(dòng)完成清理工作柑土,無須手動(dòng)干預(yù)
2.在需要管理一些資源比如文件蜀肘,數(shù)據(jù)庫連接和鎖的編程環(huán)境中,可以在exit中定制自動(dòng)釋放資源的機(jī)制
3.提高代碼的可讀性稽屏,簡(jiǎn)潔性等等

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扮宠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狐榔,更是在濱河造成了極大的恐慌坛增,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薄腻,死亡現(xiàn)場(chǎng)離奇詭異收捣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)庵楷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門罢艾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尽纽,你說我怎么就攤上這事咐蚯。” “怎么了弄贿?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵春锋,是天一觀的道長。 經(jīng)常有香客問我差凹,道長期奔,這世上最難降的妖魔是什么侧馅? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮呐萌,結(jié)果婚禮上馁痴,老公的妹妹穿的比我還像新娘。我一直安慰自己搁胆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布邮绿。 她就那樣靜靜地躺著渠旁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪船逮。 梳的紋絲不亂的頭發(fā)上顾腊,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音挖胃,去河邊找鬼杂靶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酱鸭,可吹牛的內(nèi)容都是我干的吗垮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼凹髓,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烁登!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蔚舀,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤饵沧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赌躺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狼牺,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年礼患,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了是钥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缅叠,死狀恐怖咏瑟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痪署,我是刑警寧澤码泞,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站狼犯,受9級(jí)特大地震影響余寥,放射性物質(zhì)發(fā)生泄漏领铐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一宋舷、第九天 我趴在偏房一處隱蔽的房頂上張望绪撵。 院中可真熱鬧,春花似錦祝蝠、人聲如沸音诈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽细溅。三九已至,卻和暖如春儡嘶,著一層夾襖步出監(jiān)牢的瞬間喇聊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工蹦狂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留誓篱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓凯楔,卻偏偏與公主長得像窜骄,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摆屯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一、私有屬性 我們大家知道在類里面定義的屬性名前加兩個(gè)下劃線就是私有屬性富弦,它是不能在外面被訪問的沟娱,如下:class...
    IIronMan閱讀 360評(píng)論 0 1
  • 寫在前面的話 代碼中的# > 表示的是輸出結(jié)果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 2,753評(píng)論 0 8
  • 一、Python簡(jiǎn)介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡(jiǎn)介】: Python 是一個(gè)...
    _小老虎_閱讀 5,744評(píng)論 0 10
  • 高階函數(shù):將函數(shù)作為參數(shù) sortted()它還可以接收一個(gè)key函數(shù)來實(shí)現(xiàn)自定義的排序,reversec參數(shù)可反...
    royal_47a2閱讀 684評(píng)論 0 0
  • 還記得小學(xué)時(shí)剛開始學(xué)寫作文時(shí)腕柜,老師大多會(huì)用這個(gè)字讓我們作為敲門磚济似,撬開文學(xué)寫作的大門。那時(shí)怎么寫我已經(jīng)毫無印象盏缤,然...
    神貓法師閱讀 239評(píng)論 0 0