一般我們操作文件的時(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ì)象的類中聲明enter和exit方法
下面我們看看怎么定義自己的上下文管理器:
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è)陬愔屑尤氘惓4a塊锋喜,看看什么情況些己,是否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)潔性等等