Python進階:With語句和上下文管理器ContextManager
Python中的with語句和上下文管理器,是從2.5版本開始加入到Python語法中的。
老規矩,文中代碼整理後上傳到Github:xianhu/LearnPython
上下文管理器(Context Manager)
上下文管理器是指在一段代碼執行之前執行一段代碼,用於一些預處理工作;執行之後再執行一段代碼,用於一些清理工作。比如打開文件進行讀寫,讀寫完之後需要將文件關閉。又比如在資料庫操作中,操作之前需要連接資料庫,操作之後需要關閉資料庫。在上下文管理協議中,有兩個方法__enter__和__exit__,分別實現上述兩個功能。
with語法
講到上下文管理器,就不得不說到python的with語法。基本語法格式為:
with EXPR as VAR:n BLOCKn
這裡就是一個標準的上下文管理器的使用邏輯,稍微解釋一下其中的運行邏輯:
(1)執行EXPR語句,獲取上下文管理器(Context Manager)
(2)調用上下文管理器中的__enter__方法,該方法執行一些預處理工作。
(3)這裡的as VAR可以省略,如果不省略,則將__enter__方法的返回值賦值給VAR。
(4)執行代碼塊BLOCK,這裡的VAR可以當做普通變數使用。
(5)最後調用上下文管理器中的的__exit__方法。
(6)__exit__方法有三個參數:exc_type, exc_val, exc_tb。如果代碼塊BLOCK發生異常並退出,那麼分別對應異常的type、value 和 traceback。否則三個參數全為None。
(7)__exit__方法的返回值可以為True或者False。如果為True,那麼表示異常被忽視,相當於進行了try-except操作;如果為False,則該異常會被重新raise。
實例:自己實現打開文件操作
# 自定義打開文件操作nclass MyOpen(object):nn def __init__(self, file_name):n """初始化方法"""n self.file_name = file_namen self.file_handler = Nonen returnnn def __enter__(self):n """enter方法,返回file_handler"""n print("enter:", self.file_name)n self.file_handler = open(self.file_name, "r")n return self.file_handlernn def __exit__(self, exc_type, exc_val, exc_tb):n """exit方法,關閉文件並返回True"""n print("exit:", exc_type, exc_val, exc_tb)n if self.file_handler:n self.file_handler.close()n return Truenn# 使用實例nwith MyOpen("python_base.py") as file_in:n for line in file_in:n print(line)n raise ZeroDivisionErrorn# 代碼塊中主動引發一個除零異常,但整個程序不會引發異常n
代碼很簡單,也很容易理解,這裡不做過多解釋。
內置庫contextlib的使用
Python提供內置的contextlib庫,使得上線文管理器更加容易使用。其中包含如下功能:
(1)裝飾器contextmanager。該裝飾器將一個函數中yield語句之前的代碼當做__enter__方法執行,yield語句之後的代碼當做__exit__方法執行。同時yield返回值賦值給as後的變數。
@contextlib.contextmanagerndef open_func(file_name):n # __enter__方法n print(open file:, file_name, in __enter__)n file_handler = open(file_name, r)nn yield file_handlernn # __exit__方法n print(close file:, file_name, in __exit__)n file_handler.close()n returnnnwith open_func(python_base.py) as file_in:n for line in file_in:n print(line)n
(2)closing類。該類會自動調用傳入對象的close方法。使用實例如下:
class MyOpen2(object):n def __init__(self, file_name):n """初始化方法"""n self.file_handler = open(file_name, "r")n returnnn def close(self):n """關閉文件,會被自動調用"""n print("call close in MyOpen2")n if self.file_handler:n self.file_handler.close()n returnnnwith contextlib.closing(MyOpen2("python_base.py")) as file_in:n passn
這裡會自動調用MyOpen2的close方法。我們查看contextlib.closing方法,內部實現為:
class closing(object):n """Context to automatically close something at the end of a block."""n def __init__(self, thing):n self.thing = thingn def __enter__(self):n return self.thingn def __exit__(self, *exc_info):n self.thing.close()n
closing類的__exit__方法自動調用傳入的thing的close方法。
(3)nested類。該類在Python2.7之後就刪除了。原本該類的作用是減少嵌套,但是Python2.7之後允許如下的寫法:
with open("aaa", "r") as file_in, open("bbb", "w") as file_out:n passn
參考:https://www.python.org/dev/peps/pep-0343/
=============================================================
作者主頁:笑虎(Python愛好者,關注爬蟲、數據分析、數據挖掘、數據可視化等)
作者專欄主頁:擼代碼,學知識 - 知乎專欄
作者GitHub主頁:擼代碼,學知識 - GitHub
歡迎大家拍磚、提意見。相互交流,共同進步!
==============================================================
推薦閱讀: