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