【Python】with及上下文管理器的原理和應用

這篇博客主要總結with用法,自定義上下文管理器,以及__exit__的參數相關內容。

with 語句是 Pyhton 提供的一種簡化語法,適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源,with 語句主要是爲了簡化代碼操作。

with:文件使用後自動關閉

# 創建一個文件test.txt,若存在則打開,寫入Hello Python
# 創建/打開文件
f = open('test.txt', 'w')
f.write("Hello Python")
# 關閉這個文件
f.close()
 
 
# 使用with
with open('test.txt', 'w') as f:
    f.write('Python')
可以發現:通過 with 語句在編寫代碼時,會使代碼變得更加簡潔,不用再去關閉文件。

with的執行過程:

在執行 with 語句時,首先執行 with 後面的 open 代碼

執行完代碼後,會將代碼的結果通過 as 保存到 f 中

然後在下面實現真正要執行的操作

在操作後面,並不需要寫文件的關閉操作,文件會在使用完後自動關閉

 

with的執行原理

實際上,在文件操作時,並不是不需要寫文件的關閉,而是文件的關閉操作在 with 的上下文管理器中的協議方法裏已經寫好了。當文件操作執行完成後, with語句會自動調用上下文管理器裏的關閉語句來關閉文件資源。

上下文管理器

ContextManager ,上下文是 context 直譯的叫法,在程序中用來表示代碼執行過程中所處的前後環境。上下文管理器中有 __enter__ 和 __exit__ 兩個方法,以with爲例子,__enter__ 方法會在執行 with 後面的語句時執行,一般用來處理操作前的內容。比如一些創建對象,初始化等;__exit__ 方法會在 with 內的代碼執行完畢後執行,一般用來處理一些善後收尾工作,比如文件的關閉,數據庫的關閉等。

自定義一個上下文管理器,模擬with文件操作
class MyOpen(object):
    def __init__(self,path,mode):
        # 記錄要操作的文件路徑和模式
        self.__path = path
        self.__mode = mode
 
    def __enter__(self):
        print('代碼執行到了__enter__......')
        # 打開文件
        self.__handle = open(self.__path,self.__mode)
        # 返回打開的文件對象引用, 用來給  as 後的變量f賦值
        return self.__handle
 
    # 退出方法中,用來實現善後處理工作
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('代碼執行到了__exit__......')      
        self.__handle.close()
 
# a+ 打開一個文件用於讀寫。如果該文件已存在,文件指針將會放在文件的結尾。文件打開時會是追加模式。如果該文件不存在,創建新文件用於讀寫。
with MyOpen('test.txt','a+') as f:
    # 創建寫入文件
    f.write("Hello Python!!!")
    print("文件寫入成功")
執行結果:

通過執行順序,可以看到文件寫入操作執行完之後,自動調用了__exit__方法,做了善後處理工作。

 

__exit__方法的參數

__exit__ 方法中有三個參數,用來接收處理異常,如果代碼在運行時發生異常,異常會被保存到這裏。 

exc_type : 異常類型

exc_val : 異常值

exc_tb : 異常回溯追蹤

# 編寫兩個數做除法的程序,然後給除數穿入0
class MyCount(object):
    # 接收兩個參數
    def __init__(self,x, y):
        self.__x = x
        self.__y = y
    # 返回一個地址(實質是被as後的變量接收),實例對象就會執行MyCount中的方法:div()
    def __enter__(self):
        print('代碼執行到了__enter__......')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("代碼執行到了__exit__......")
        if exc_type == None:
            print('程序沒問題')
        else:
            print('程序有問題,如果你能你看懂,問題如下:')
            print('Type: ', exc_type)
            print('Value:', exc_val)
            print('TreacBack:', exc_tb)
 
        # 返回值決定了捕獲的異常是否繼續向外拋出
        # 如果是 False 那麼就會繼續向外拋出,程序會看到系統提示的異常信息
        # 如果是 True 不會向外拋出,程序看不到系統提示信息,只能看到else中的輸出
        return  True
 
    def div(self):
        print("代碼執行到了除法div")
        return self.__x / self.__y
 
 
with MyCount(1, 0) as mc:
    mc.div()
執行結果:

可以看到,系統沒有拋出異常,而是__exit__捕獲到了異常,並按照我們的方式進行了拋出。
--------------------- 
作者:高巖_deal 
來源:CSDN 
原文:https://blog.csdn.net/mydistance/article/details/82730014 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章