with語句的用法詳解
with語句的使用可以簡化try...except...finally語句
當我們之前從磁碟上讀取某個檔案時,會使用try...except...finally語句,這樣雖然解決了當檔案未找到時,程式會捕獲異常,並不會突然退出,並列印我們自己定義的錯誤資訊,但是這樣的寫法未必過於冗餘
try: file = open("diary.txt", "r", encoding="utf-8") for line in file.readlines(): print(line) except IOError: print("檔案沒有找到") finally: file.close()
於是我們可以使用with語句來優化這個程式碼,with語句不管在執行程式碼語句出現異常退出的情況都會執行一些”清理“操作,資源回收,流的關閉等一些操作
with open("diary.txt", encoding="utf-8") as file: for line in file.readlines(): print(line)
但是為什麼這樣的語法就能夠實現try...except...finally類似的功能呢?
我們把這些語句拆解成多個模組
with EXPR as VAR:
BLOCK
EXPR:任意表達式,用於生成上下文管理器(context manager)
as VAR:可選
BLOCK:程式碼塊
在介紹執行流程之前,我們先介紹一下上下文管理器
上下文管理器:支援上下文協議,實現上下文協議中所規定的__enter__與__exit__方法,通常使用with語句來呼叫上下文管理器,同時會執行上下文管理器中的__enter__與__exit__方法
語句執行流程:
1.執行EXPR語句,生成上下文管理器,獲取上下文管理器中的__enter__與__exit__方法
2.執行__enter__方法,如果有as VAR語句,則會把__enter__方法執行後的返回值賦值給VAR
3.執行BLOCK程式碼塊,不管程式碼塊中是否出現異常都會最後都會執行__exit__方法
我們嘗試自己製作一個可以上下文管理器並驗證這個執行流程
1 class ContextManager(): 2 def __enter__(self): 3 print("執行了上下文管理器中的 enter 方法") 4 return "enter 方法的返回值" 5 6 def __exit__(self, exc_type, exc_val, exc_tb): 7 print("exit方法執行完成") 8 9 10 with ContextManager() as result: 11 print("程式碼塊執行111") 12 print("程式碼塊執行222") 13 print(result) 14 raise IOError 15 print("程式碼塊執行333")
執行結果:
從執行結果可以看出首先執行了ContextManager()生成了上下文管理器物件,隨後呼叫了__enter__方法,將返回值賦值給result,隨後執行程式碼塊,其中也列印了result的值,
14行程式碼丟擲異常,後面的程式碼沒有執行,但是最後執行了__exit__方法。
Traceback (most recent call last): File "D:/PyCharmWorkspace/pytest_learning/context_manager.py", line 14, in <module> raise IOError OSError 執行了上下文管理器中的 enter 方法 程式碼塊執行111 程式碼塊執行222 enter 方法的返回值 exit方法執行完成