一、python上下文介紹:
python中的上下文,指的就是代碼所執行的環境狀態,或者運行的場景
python上下文管理器規定了某個對象的使用范圍,當進入或者離開了使用范圍,會有相應的操作,多用於資源的分配和釋放上,即在開始時分配資源,結束時釋放資源。
如文件的讀寫,在讀寫前,需要先打開文件,在讀寫完成后,需要關閉文件。再如數據庫的操作,在操作前,需要先連接數據庫,結束后,需要釋放連接等。
二、上下文管理器的使用:
下面看看資源的創建和釋放場景(以數據庫的操作為例):

class DataBase(object): def __init__(self): self.connect = False def connect(self): self.connect = True def close(self): self.connect = False def query(self): if self.connect: return "query data" else: raise ValueError("db not connected") def handle_query(): db = DataBase() db.connect() print("db query:", db.query()) db.close() if __name__=="__main__": handle_query()
上述代碼很簡單,針對數據類DataBase,我們提供了connect,close,query三個常規的db交互接口。
普通的功能實現是沒有問題的,但是我們都知道,在web交互中,數據庫的操作是最頻繁的,也就是說,我們每次在操作數據庫的時候,都需要進行數據庫類實例化,連接,關閉操作。顯然這個和pythonic不符合的。
然后為了上面的代碼更加的優雅,我們想到了python中的一個黑魔法,裝飾器。
我們可以將上述代碼數據庫的連接和關閉封裝成一個閉包,然后通過裝飾器對上面的代碼進行改寫:
class DataBase(object): def __init__(self): self.connect = False def conn(self): self.connect = True def close(self): self.connect = False def query(self): if self.connect: return "query data" else: raise ValueError("db not connected") def dbconn(fun): def wrapper(*args, **kwargs): db = DataBase() db.conn() fun(db, *args, **kwargs) db.close() return wrapper @dbconn def handle_query(db=None): print("query:", db.query) if __name__=="__main__": handle_query()
我們封裝了一個dbconn的裝飾器,通過使用@dbconn裝飾器,實現了數據庫連接和釋放的代碼的復用,對比上面的代碼,可以發現,使用裝飾器相對優雅了很多
但是每個裝飾器都需要先定義一下db的資源句柄,看起來還是沒有那么優雅。下面我們通過上下文管理器的方式實現數據庫操作:
class DataBase(object): def __init__(self): self.connect = False def conn(self): self.connect = True def close(self): self.connect = False def query(self): return "query data" def __enter__(self): self.conn() return self def __exit__(self, exc_type, exc_value, exc_db): self.close() def handle_query(): with DataBase() as db: print("query:", db.query) if __name__=="__main__": handle_query()
對比上面的上下文管理器實現數據庫操作的代碼,可以發現,在定義數據庫類DataBase的時候,雖然多寫了幾行代碼,但是我們的代碼可讀性變得更好了。
三、上下文管理協議
實現了上下文協議的對象叫上下文管理器
那么什么是上下文協議呢?即實現了__enter__() 和__exit__()方法,也就是實現了上下文管理協議
上下文表達式必須要返回一個上下文管理器對象
代碼結構如下:
class Context: def __init__(self, filename, fileMode="w"): self.obj = open(filename, fileMode) def __enter__(self): return self.obj def __exit__(self, exc_type, exc_value, exc_db): self.obj.close() with Context("C:\Users\admin\Desktop\1.txt") as f: f.write("something")
如上圖,Context類實現了__enter__和__exit__兩個上下文管理器協議,當Context被調用或實例化的時候,創建了上下文管理器
配合with語句使用的時候,上下文管理器會自動調用__enter__方法,然后進入運行時上下文環境,並將__enter__函數返回值賦值給as從句變量 f
當f.write("something")執行完畢退出with語句塊或者是出現異常導致程序退出時,會執行__exit__方法,並把異常信息傳遞給后面的參數exc_type, exc_value, exc_db
如果__exit__方法返回True,則with語句塊不會顯示的拋出異常,程序終止
如果__exit__方法返回None或者False,異常會被主動拋出,程序終止。
四、上下文管理器工具
python為了使代碼更加的優雅,還提供了一個模塊contextlib 來實現更函數式的上下文管理器
from contextlib import contextmanager @contextmanager def context(filename, fileMode="w"): f = open(filename, fileMode) try: yield f except Exception as e: print(e) finally: f.close() with context("C:\\Users\\admin\\Desktop\\1.txt") as f: f.write("write something1")
上圖通過contextmanager裝飾器定義了一個上下文管理器函數context,通過with context("C:\\Users\\admin\\Desktop\\1.txt") 返回上下文管理器對象
然后調用函數隱式的__enter__函數,並將結果通過yield返回
然后通過返回的文件對象,執行文件的寫入操作
寫入操作結束,退出上下文環境,執行了f.close()
五、with語句執行過程總結:
1)執行上下文表達式 Context("C:\Users\admin\Desktop\1.txt"),獲得上下文管理器
2)加載上下文管理器的__enter__ 和 __exit__,以備后續調用
3)調用__enter__(),方法,並返回文件對象 f 賦值給as從句變量
4)執行with語句塊的子語句塊f.write("something")
5)調用上下文管理器的__exit__(),如果是由於異常導致程序退出的,將type, value 和traceback作為參數傳遞給exit(),否則傳三個None
如果__exit__()返回值等於False,則異常會被顯示的拋出,程序終止,否則,異常會被無視,繼續執行。
上下文管理器主要用於多線程/進程的變成中,因為涉及到上下文的切換