Python實(shí)現(xiàn)上下文管理器的示例代碼
在 Web 開(kāi)發(fā)中,我們經(jīng)常需要對(duì)資源進(jìn)行操作,如從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)、日志文件的寫(xiě)入等。這些都是常見(jiàn)的資源操作,而資源是有限的,所以當(dāng)我們對(duì)資源操作完成以后就需要對(duì)其進(jìn)行釋放。如果資源沒(méi)有得到釋放,當(dāng)資源占用數(shù)達(dá)到了操作系統(tǒng)的限定數(shù),就會(huì)導(dǎo)致程序崩潰。
在文件操作過(guò)程中,為了保證文件操作完成后關(guān)閉文件資源,我們可能會(huì)寫(xiě)出這樣的代碼:
try:
f = open('demo.txt', 'w')
f.write('hello')
finally:
f.close()這是一個(gè)典型的資源操作的例子,不過(guò) Python 提供了 with 語(yǔ)句可以對(duì)以上代碼進(jìn)行簡(jiǎn)化:
with open('demo.txt', 'w') as f:
f.write('hello')可以看到,用 with 重寫(xiě)過(guò)的代碼中并沒(méi)有顯式的調(diào)用 f.close() 方法來(lái)關(guān)閉文件,但當(dāng) with 代碼塊執(zhí)行完以后,文件還是會(huì)被正常關(guān)閉的。即使在調(diào)用 f.write() 時(shí)產(chǎn)生異常,文件同樣會(huì)被關(guān)閉。
這就是 with 語(yǔ)句的強(qiáng)大之處,它不僅可以簡(jiǎn)化我們的代碼,并且能夠自動(dòng)釋放資源。其實(shí)這就是所謂的 上下文管理器。能夠自動(dòng)分配和釋放資源正是上下文管理器最強(qiáng)大的地方。
open 函數(shù)能夠通過(guò) with 語(yǔ)句完成自動(dòng)化的上下文管理,實(shí)際上,我們同樣可以實(shí)現(xiàn)自定義的 上下文管理器。Python 為我們實(shí)現(xiàn)自定義 上下文管理器 提供了一個(gè)統(tǒng)一的接口——上下文管理協(xié)議。只要我們按照 Python 的規(guī)定實(shí)現(xiàn)了 上下文管理協(xié)議,就能夠?qū)崿F(xiàn)自定義的 上下文管理器。
基于類實(shí)現(xiàn)上下文管理器
我們知道,在 Python 中有很多雙下劃線開(kāi)頭、雙下劃線結(jié)尾的方法,我們通常稱作 魔法方法。基于類的上下文管理器就是通過(guò) __enter__、__exit__ 兩個(gè) 魔法方法 來(lái)實(shí)現(xiàn)的。
下面通過(guò)一個(gè)自定義的文件上下文管理器類來(lái)介紹上下文管理器的實(shí)現(xiàn):
class MyFileManager(object):
def __init__(self, filename, mode):
print('call __init__')
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print('call __enter__')
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print('call __exit__')
if self.file:
self.file.close()
with MyFileManager('demo.txt', 'w') as f:
print('write file')
f.write('test')代碼執(zhí)行結(jié)果如下:
call __init__
call __enter__
write file
call __exit__
可以看到,只需要十幾行的代碼,我們就實(shí)現(xiàn)了一個(gè)上下文管理器。而它的效果和 with open 并無(wú)兩樣。由代碼執(zhí)行結(jié)果我們可以分析出代碼的執(zhí)行順序。首先在 with 語(yǔ)句后面實(shí)例化 MyFileManager 類,實(shí)例化過(guò)程中會(huì)調(diào)用類的 __init__ 方法,接著自動(dòng)調(diào)用了 __enter__ 方法并拿到返回值,即文件對(duì)象,as f 就是將 __enter__ 方法返回的文件對(duì)象賦值給 f 變量,然后執(zhí)行 with 代碼塊中的代碼對(duì)文件進(jìn)行寫(xiě)入,當(dāng)程序?qū)⒁顺?with 代碼塊時(shí),就會(huì)自動(dòng)調(diào)用 MyFileManager 類的 __exit__ 方法。
以上就是自定義 上下文管理器 的執(zhí)行邏輯。要實(shí)現(xiàn) 上下文管理器,__enter__、__exit__ 兩個(gè)方法是必須實(shí)現(xiàn)的。其中 __enter__ 方法用來(lái)獲取資源,所以它將在 with 語(yǔ)句代碼塊開(kāi)始執(zhí)行之前被調(diào)用,__exit__ 方法用來(lái)釋放資源,它將在 with 語(yǔ)句代碼塊執(zhí)行結(jié)束將要退出時(shí)被調(diào)用。
需要額外注意的是 __exit__ 方法的參數(shù),相比 __enter__ 方法它多了三個(gè)參數(shù),這三個(gè)參數(shù)的作用分別是:
- exc_type: 異常類型
- exc_val: 異常的值
- exc_tb:異常棧
當(dāng)執(zhí)行 with 語(yǔ)句塊中的代碼,程序有可能會(huì)拋出異常,而 __exit__ 方法的這三個(gè)參數(shù),就會(huì)包含 with 語(yǔ)句塊中產(chǎn)生的異常信息。所以 __exit__ 方法還為我們提供了另外一個(gè)功能,即可以在此方法內(nèi)部對(duì)異常進(jìn)行處理,由開(kāi)發(fā)者來(lái)決定異常的處理方式。
class MyFileManager(object):
def __init__(self, filename, mode):
print('call __init__')
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print('call __enter__')
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print('call __exit__')
if self.file:
self.file.close()
if exc_type:
print(f'exc_type: {exc_type}')
print(f'exc_val: {exc_val}')
print(f'exc_tb: {exc_tb}')
return True
with MyFileManager('demo.txt', 'w') as f:
raise Exception('write error')代碼執(zhí)行結(jié)果如下:
call __init__
call __enter__
call __exit__
exc_type: <class 'Exception'>
exc_val: write error
exc_tb: <traceback object at 0x036D08A0>
可以看到,在 with 代碼塊中拋出的異常已經(jīng)被 __exit__ 方法捕獲了。需要注意的是,在 __exit__ 方法內(nèi)部的最后還返回了 True,這代表異常已經(jīng)處理,而不需要繼續(xù)向上拋出,如果沒(méi)有返回 True,那么異常將繼續(xù)向上拋出,最終被 Python 解釋器捕獲到,繼而終止程序。
基于生成器實(shí)現(xiàn)上下文管理器
Python 還提供了另一種基于生成器來(lái)實(shí)現(xiàn) 上下文管理器 的方法。示例代碼如下:
from contextlib import contextmanager
@contextmanager
def my_file_manager(filename, mode):
print('open file')
f = open(filename, mode)
yield f
print('close file')
f.close()
with my_file_manager('demo.txt', 'w') as f:
print('write file')
f.write('demo')代碼執(zhí)行結(jié)果如下:
open file
write file
close file
基于生成器的 上下文管理器 可以讓我們不必通過(guò)類來(lái)實(shí)現(xiàn),只需要一個(gè)打上 @contextmanager 裝飾器的生成器函數(shù)即可。相比類實(shí)現(xiàn)的方式代碼更加簡(jiǎn)潔,不過(guò)理解上來(lái)說(shuō)沒(méi)有類實(shí)現(xiàn)的方式來(lái)的清晰。但這種風(fēng)格寫(xiě)起來(lái)更加 Pythonic。
其實(shí) 上下文管理器 的本質(zhì)是能夠在我們需要執(zhí)行的一段代碼之前和之后分別執(zhí)行一段邏輯。這剛好能夠利用生成器函數(shù)的特性,在 yield 關(guān)鍵字之前執(zhí)行預(yù)處理工作,然后代碼執(zhí)行到 yield 處時(shí)讓出函數(shù)的控制權(quán),這時(shí)候再執(zhí)行我們需要執(zhí)行的一段代碼邏輯,最后 with 語(yǔ)句幫我們把執(zhí)行控制權(quán)交回到生成器函數(shù)內(nèi)部,執(zhí)行一些清理工作。
最后,一個(gè)小知識(shí)點(diǎn),一個(gè) with 語(yǔ)句實(shí)際上可以同時(shí)處理多個(gè)上下文管理器。
with open('a.txt', 'w') as a, open('b.txt', 'w') as b:
a.write('hello')
b.write('world')到此這篇關(guān)于Python實(shí)現(xiàn)上下文管理器的示例代碼的文章就介紹到這了,更多相關(guān)Python上下文管理器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python爬蟲(chóng)MeterSphere平臺(tái)執(zhí)行報(bào)告使用實(shí)戰(zhàn)
這篇文章主要為大家介紹了python爬蟲(chóng)MeterSphere平臺(tái)執(zhí)行報(bào)告使用實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
解決Python print輸出不換行沒(méi)空格的問(wèn)題
今天小編就為大家分享一篇解決Python print輸出不換行沒(méi)空格的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
Python實(shí)現(xiàn)的HMacMD5加密算法示例
這篇文章主要介紹了Python實(shí)現(xiàn)的HMacMD5加密算法,簡(jiǎn)單說(shuō)明了HMAC-MD5加密算法的概念、原理并結(jié)合實(shí)例形式分析了Python實(shí)現(xiàn)HMAC-MD5加密算法的相關(guān)操作技巧,,末尾還附帶了Java實(shí)現(xiàn)HMAC-MD5加密算法的示例,需要的朋友可以參考下2018-04-04
Python&Matlab實(shí)現(xiàn)灰狼優(yōu)化算法的示例代碼
灰狼優(yōu)化算法是一種群智能優(yōu)化算法,它的獨(dú)特之處在于一小部分擁有絕對(duì)話語(yǔ)權(quán)的灰狼帶領(lǐng)一群灰狼向獵物前進(jìn)。本文具體介紹了灰狼優(yōu)化算法的兩種實(shí)現(xiàn)示例代碼,需要的可以參考一下2022-03-03
python庫(kù)TextDistance量化文本之間的相似度算法探究
這篇文章主要為大家介紹了python庫(kù)TextDistance量化文本之間的相似度算法探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Python偏函數(shù)實(shí)現(xiàn)原理及應(yīng)用
這篇文章主要介紹了Python偏函數(shù)實(shí)現(xiàn)原理及應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
淺談Python用QQ郵箱發(fā)送郵件時(shí)授權(quán)碼的問(wèn)題
下面小編就為大家分享一篇淺談Python用QQ郵箱發(fā)送郵件時(shí)授權(quán)碼的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Python爬蟲(chóng)請(qǐng)求模塊Urllib及Requests庫(kù)安裝使用教程
requests和urllib都是Python中常用的HTTP請(qǐng)求庫(kù),使用時(shí)需要根據(jù)實(shí)際情況選擇,如果要求使用簡(jiǎn)單、功能完善、性能高的HTTP請(qǐng)求庫(kù),可以選擇requests,如果需要兼容性更好、功能更加靈活的HTTP請(qǐng)求庫(kù),可以選擇urllib2023-11-11

