您好,登錄后才能下訂單哦!
這篇“Python上下文管理器是什么及怎么使用”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Python上下文管理器是什么及怎么使用”文章吧。
上下文管理器是一個對象,它定義了在執行with語句時要建立的運行時上下文。上下文管理器是為代碼塊所執行的上下文環境自動處理進入和退出所需的運行時。上下文管理器通常使用with語句調用,但也可以通過直接調用它們的方法來使用。
上下文管理器的典型用途包括保存和恢復各種全局狀態,鎖定和解鎖資源,關閉打開的文件,等等。
with語句用于上下文管理器定義的方法包裝塊的執行。這允許封裝常見的try…except…finally使用模式以方便重用。與傳統的try…except…finally塊相比,with語句提供了更短且可重用的代碼。
在Python標準庫中,許多類都支持with語句。一個非常常見的例子是內置的open()函數,它提供了使用with語句處理文件對象的模式。
下面是with語句的一般語法:
with expression as target: # 使用target # 來處理事情
我們看一個使用open()函數的例子。在當前項目的files文件夾中有一個文本文件。文件名為color_names.txt,其中包含一些顏色名稱(可自行提供一些文本內容)。我們希望通過使用open()函數和with語句打開并打印該文件中的內容。代碼示例如下:
import os fileDirPath = os.getcwd()+os.sep+"ctxManager"+os.sep #自定義文件路徑 # 指定文件路徑和名稱 path = fileDirPath+'files/color_names.txt' # with 語句 with open(path, mode='r') as file: # 讀取文件內容 print(file.read())
運行程序輸出結果如下
red orange yellow green blue white black
在上面清單中,所看到是with語句的一個常見用例。我們使用open()函數打開給定的路徑(path)上的文件,且open()函數以只讀模式返回文件對象。然后代碼中使用這個文件對象讀取并通過代碼:print(file.read())將其內容打印輸出。
上面示例是上下文管理器的一個典型用法。為了更好地理解和應用上下文管理器,我們還得繼續往下看。
上下文管理器協議(Context Manager Protocol),說白了就是上下文管理器的處理機制,或說預定的規約標準。這部分內容也可查看這里:Python核心協議。為了閱讀的獨立性,這里也再說一說。
Python的with語句支持由上下文管理器定義的運行時上下文的概念。這是通過一對方法實現的,它們允許用戶定義的類定義運行時上下文,該上下文在語句體執行之前進入,并在語句結束時退出。
前所提到的這些方法稱為上下文管理器協議。來具體看一下這兩個方法:
1)__enter__(self)
該方法由with語句調用,以進入與當前對象相關的運行時上下文。with語句將此方法的返回值綁定到語句的as子句中指定的目標(如果有的話)。
上例中返回的上下文管理器的是文件對象。在背后,文件對象從__enter__()返回其本身,以允許open()被用作with語句中的上下文表達式。
2)__exit__(self, exc_type, exc_value, traceback):
當執行離開with代碼塊時調用此方法。它退出與此對象相關的運行時上下文。參數描述了導致退出上下文的異常信息。如果沒有異常而退出上下文,那么所有三個參數都將為None。
如果提供了異常,并且希望該方法抑制該異常(即,阻止它被傳播),那么它應該返回一個True值。否則,異常將在退出此方法時正常處理。__exit__()方法返回一個布爾值,可以是True或False。
使用上下文管理器協議中的方法執行with語句的過程如下:
with EXPRESSION as TARGET: SUITE
計算上下文表達式(EXPRESSION)以獲得上下文管理器。
加載上下文管理器的__enter__()以供隨后使用。
加載上下文管理器的__exit__()以供隨后使用。
調用上下文管理器的__enter__()方法。
如果在with語句中包含了一個TARGET,則會將__enter__()的返回值賦給它。
執行套件(with語句作用域中的代碼塊)。
調用上下文管理器的__exit__()方法。如果異常導致套件退出,則其類型、值和回溯將作為參數傳遞給__exit__()。否則,將提供三個None參數。
如果套件因異常以外的任何原因退出,則會忽略__exit__()的返回值,并在所執行退出類型的正常位置繼續執行后續代碼(若有)。
現在我們了解了上下文管理器協議背后的基本思想,讓我們在一個類中實現它。這個類將是我們的上下文管理器,并稍后在with語句中使用它。
定義的上下文管理器類參考示例清單如下:
# 自定義上下文管理器類 class CustomContextManager: # 初始化方法init -> 定義一些變量 def __init__(self, path, mode): self.path = path self.mode = mode self.file = None # __enter__ method -> open the file def __enter__(self): self.file = open(self.path, self.mode) return self.file # exit method to close the file def __exit__(self, exc_type, exc_value,exc_traceback): self.file.close()
我們的CustomContextManager類實現了成為上下文管理器的必要方法:__enter__和__exit__。
在__init__方法中,它定義了三個實例變量來存儲路徑、模式和文件對象。
在__enter__方法中,它使用內置的open()函數打開指定路徑中的文件。由于open()函數返回file對象,我們將其賦值給self.file屬性。
在__exit__方法中,我們將文件關閉:self.file.close()。
__exit__方法接受三個參數,它們是上下文管理器協議所需要的。
現在我們可以在with語句中使用自定義上下文管理器。
使用自定義的類上下文管理器的示例(和我們前面的示例雷同):
# 應用示例 import os fileDirPath = os.getcwd()+os.sep+"ctxManager"+os.sep # 在with語句中使用自定義上下文管理器 file_path = fileDirPath + 'files/color_names.txt' with CustomContextManager(path=file_path, mode='r') as file: #輸出文件file內容 print(file.read())
運行輸出結果這里不再贅述。簡單解釋一下代碼。
上面清單中,在with語句中使用CustomContexManager類,通過它來讀取文件內容并打印出來。下面是這個自定義上下文管理器幕后的故事:
1)在with行,調用類CustomContextManager的方_enter__法
2) __enter__方法打開文件并返回它。
3)我們將打開的文件簡單地命名為file。
4)在with語句塊中,讀取文件內容并將其打印出來。
5)with語句自動調用__exit__方法。
6)__exit__方法關閉文件。
我們再來定義另一個上下文管理器類。這次我們想打印指定文件夾中的文件列表。
參考實現的代碼清單如下:
class ContentList: '''Prints the content of a directory''' def __init__(self, directory): self.directory = directory def __enter__(self): return os.listdir(self.directory) def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is not None: print("Error getting directory list.") return True # 輸出項目目錄下的內容 project_directory = '.' with ContentList(project_directory) as directory_list: print(directory_list)
在代碼清單中,我們定義了一個新的上下文管理器。這個類的名字是ContentList。為什么它是一個上下文管理器?因為它實現了上下文管理器協議(__enter__和__exit__方法)。
我們將目錄路徑作為類構造函數__init__方法中的參數。
在__enter__方法中,只需調用os模塊中的listdir()方法,就可以獲得該目錄中的內容列表:os.listdir(self.directory)。然后返回這個列表。請注意,在這個上下文管理器中我們的__enter__方法返回一個列表。
在__exit__方法中,我們檢查是否存在任何錯誤。如果我們的上下文管理器中有錯誤,exc_type、exc_val、exc_tb參數值將不會為None。因此,我們檢查exc_type是否為None以打印錯誤文本。
在with語句中使用該上下文管理器。由于它返回一個列表對象,我們只需將返回值賦值給directory_list變量。在with語句的主體中,我們打印這個列表。運行程序后在輸出中,可以看到項目目錄中的內容列表。記住,"."表示當前目錄,在我們的例子中是項目根目錄(由于項目環境不同,輸出內容可能也不一樣)。
前文中,我們學習了如何使用類語法定義上下文管理器。但是有點繁瑣和冗長。因為需要明確地實現__enter__和exit__方法,還需要處理可能的異常。所以希望Python中能有在創建上下文管理器更好的方法:基于函數的上下文管理器。
其實函數上下文管理器是使用生成器和contextlib.contextmanager裝飾器的特殊函數。 contextlib.contextmanager裝飾器負責實現上下文管理器協議。
下面就來定義一個函數型上下文管理器。
from contextlib import contextmanager # 定義上下文管理器函數 @contextmanager def function_based_context_manager(): print("進入上下文: __enter__") yield "這是個基于上下文管理器的函數" print("離開上下文: __exit__") # with語句中使用上下文管理器函數 with function_based_context_manager() as yield_text: print(yield_text)
運行程序輸出結果類似如下:
進入上下文: __enter__ 這是個基于上下文管理器的函數 離開上下文: __exit__
在上面代碼中,我們定義了一個作為上下文管理器的自定義函數。contextmanager裝飾器將常規函數轉換為全堆棧上下文管理器(自動實現上下文管理器的協議)。如果你為函數提供了@contextmanager裝飾器,就不需要擔心實現__enter__和__exit__函數。
代碼中的yield語句在基于類的上下文管理器中的__enter__方法中充當返回語句。由于我們使用了yield語句,故此,這個基于函數的上下文管理器也是生成器函數。
再來定義一個新的上下文管理器。這一次,它將以寫的模式打開一個文件并添加一些文本。示例如下:
在清單中,我們定義了一個基于函數的上下文管理器。在try塊中,它嘗試打開指定路徑中的文件,并指定了文件的默認編碼集。如果它成功地打開它,那么它將生成(返回)file_object。在finally塊中,我們檢查是否有一個file_object要關閉。如果file_object不是None,則關閉file_object。
在with語句中,我們用文件名funBasedContextManagers.txt調用上下文管理器。上下文管理器以寫模式打開該文件并返回文件對象,我們將其簡單命名為file。接著在這個文件中寫入一些文本。記住,如果這樣的文件不存在,'w'模式將創建一個空文件。
運行上面程序,若文件不存在,則生成相應名稱的文件并保持寫入的內容。若文件存在,則這種寫入文件是每次情況源文件再寫入內容的,這一點操作請注意。
像這種處理“收尾”工作的,使用上下文管理器特別方便,尤其涉及到數據庫操作方面,比如可以自己包裝一個來完成自動的關閉連接等。
以上就是關于“Python上下文管理器是什么及怎么使用”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。