Pyhton提高:with上下文管理器

賈富程發表於2018-09-27

with 語句是 Pyhton 上的一種簡化語法,with 語句是從 Python 2.5 開始引入的一種與異常處理相關的功能。

with 語句適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必需的“清理”操作,釋放資源。比如檔案使用後自動關閉、資料庫的開啟和自動關閉等。

語法格式是這樣的:

with open('test', 'w') as f:
    f.write('Python大法好')複製程式碼

通過 with 語句在編寫程式碼時,會使程式碼變得更加簡潔。在編寫程式碼時,不用再顯示的去關閉檔案。

語句的執行過程:

  1. 在執行 with 語句時,首先執行 with 後面的 open 程式碼
  2. 執行完程式碼後,會將程式碼的結果通過 as 儲存到 f 中
  3. 然後在下面實現真正要執行的操作
  4. 在操作後面,並不需要寫檔案的關閉操作,檔案會在使用完後自動關閉

實際上,在檔案操作時,並不是不需要寫檔案的關閉,而是檔案的關閉操作在 with 的上下文管理器中的協議方法裡已經寫好了。

當檔案操作執行完成後, with語句會自動呼叫上下文管理器裡的關閉語句來關閉檔案資源。

with 語句在執行時,需要呼叫上下文管理器中的 __enter__ 和 __exit__ 兩個方法。

__enter__ 方法會在執行 with 後面的語句時執行,一般用來處理操作前的內容。比如一些建立物件,初始化等。

__exit__ 方法會在 with 內的程式碼執行完畢後執行,一般用來處理一些善後收尾工作,比如檔案的關閉,資料庫的關閉等。

在自定義上下文管理器時,只需要在類中實現 __enter__ 和 __exit__ 兩個方法即可。

模擬檔案開啟過程:

import time


class MyOpen(object):
    def __init__(self,file, mode):
        self.__file = file
        self.__mode = mode

    def __enter__(self):
        print('__enter__ run ... 開啟檔案')
        self.__handle = open(self.__file, self.__mode)
        return self.__handle

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__... run ... 關閉檔案')
        self.__handle.close()


with MyOpen('test','w') as f:
    f.write('Python 大法好')
    time.sleep(3)

print('over')
複製程式碼

程式執行結果:

__enter__ run ... 開啟檔案
__exit__ run ... 關閉檔案
over
複製程式碼

__exit__ 方法中有三個引數,用來接收處理異常,如果程式碼在執行時發生異常,異常會被儲存到這裡。

  • exc_type : 異常型別
  • exc_val : 異常值
  • exc_tb : 異常回溯追蹤

異常資訊的處理

當with中執行的語句發生異常時,異常資訊會被髮送到 __exit__ 方法的引數中,這時可以根據情況選擇如何處理異常。

class MyCount(object):
    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 通過 引數接收到的值,來判斷程式執行是否出現異常
        # 如果是 None ,說明沒有異常
        if exc_type == None:
            print('計算正確執行')
        else:
            # 否則出現異常,可以選擇怎麼處理異常
            print(exc_type,exc_val)
        # 返回值決定了捕獲的異常是否繼續向外丟擲
        # 如果是 False 那麼就會繼續向外丟擲,程式會看到系統提示的異常資訊
        # 如果是 True 不會向外丟擲,程式看不到系統提示資訊,只能看到else中的輸出
        return True

    def div(self):
        print(self.__x / self.__y)


with MyCount(6, 0) as mc:
    mc.div()
複製程式碼

在 __exit__函式執行異常處理時,會根據函式的返回值決定是否將系統丟擲的異常繼續向外丟擲。

如果返回值為 False 就會向外丟擲,使用者就會看到。 如果返回值為 True 不會向外丟擲,可以將異常顯示為更加友好的提示資訊。


相關文章