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