1. 程式人生 > >上下文管理器——with語句的實現

上下文管理器——with語句的實現

前言

with語句的使用給我們帶來了很多的便利,最常用的可能就是關閉一個檔案,釋放一把鎖。

既然with語句這麼好用,那我也想讓我自己寫的程式碼也能夠使用with語句,該怎麼實現?

下面具體介紹怎樣實現一個自己的with語句

使用類實現

要想使用with語句,那就要遵循with語句的使用規矩,也就是上下文管理器協議

這個協議提示我們要在類中去實現兩個特殊方法,enter(self)和exit(self, exc_type, exc_val, exc_tb)

有了這兩個方法,就可以使用with語句執行了,它的執行過程為先執行enter(self)方法,然後執行你新增在with下的程式碼,然後最後執行exit

方法

如果在執行的過程中出現了異常,那麼會自動執行exit方法,


class ErrorProduce:
    def __init__(self):
        pass

    def produce(self):
        raise RuntimeError('ERROR')

class MyWith:
    def __enter__(self):
        return ErrorProduce()

    def __exit__(self, exc_type, exc_val, exc_tb):
        if
exc_type: print("有錯") #print(exc_val) #return True else: print("完美執行") with MyWith() as E: E.produce()

將上述程式碼執行,會先打印出“有錯”,然後再打印出相關錯誤,然後程式由於錯誤終止,也就是說,在產生了錯誤後,還是執行了exit中的程式碼

如果將exit中的兩個註釋符號去掉的話,那麼再次執行程式不會報錯,但會將exc_val中的錯誤資訊打印出來,這裡相當於通過exit方法將錯誤抹除掉了,抹除方式為返回True,但也把with中丟擲錯誤後的所有程式碼都跳過了。

使用函式實現

通過引入contextlib模組中的contextmanager裝飾器,裝飾函式使函式可以使用with語句


from contextlib import contextmanager

class ErrorProduce:
    def __init__(self):
        pass

    def produce(self):
        raise RuntimeError('ERROR')

@contextmanager
def my_with():
    try:
        yield ErrorProduce()
    except Exception as e:
        print(e)
     #raise
    finally:
        print('收尾')

with my_with() as E:
    E.produce()
    print('aa')

函式中使用了yield,那麼它也就變成了生成器,通過yield來劃分相當於類中的enter和exit,yield返回的值也就是可以用作as後的值

上段程式碼中我們發現使用了try,except,finally異常處理的語句,這些語句是必要的嗎?

通過測試,如果沒有這些異常處理,那麼with語句還是可以執行的,這裡的意思是可以使用with語句,不會報不能使用with的錯誤,但是使用後,只有with下的語句中沒有丟擲異常,那麼yield語句後邊的程式碼才會執行,一旦有異常丟擲,仍然是程式執行終止。所以,一般還是要配合try,except,finally語句來構造。

還有一點要注意,使用這種方式,異常被except捕捉後,那麼這個異常就失效了,一般的我們還是會需要在except中再次將捕捉的異常丟擲,也就是在後面加上raise,因為我們使用with只是為了做好一個收尾,如關閉檔案控制代碼。或者另一種方式,我們乾脆就不寫except語句,直接寫finally語句了。


  • 文章中出現的棕色加粗的單詞,是由於markdown-here將特殊方法兩邊的下劃線轉換產生的,原意為特殊方法