1. 程式人生 > 其它 >with語句的用法詳解

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方法執行完成