Python with語句及context manager

with語句是對context manager(上下文管理者,以下簡稱CM)方法的包裝,適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源,比如文件使用後自動關閉、線程中鎖的自動獲取和釋放等。

with_item ::=  expression ["as" target]
    with-body
with open(‘x.txt’, mode=’w+’) as f:
f.write(‘xxxx\r\n’)

expression必須返回一個CM,CM的兩個方法如下:

object.__enter__(self) 
object.__exit__(self, exc_type, exc_value, traceback) 

enter會在獲取CM後調用,而exit會被存儲並在with-body運行完成或者拋出異常後調用。

class withclass:
    def __init__(self):
        self.d = dict()

    def __enter__(self):
        print('do enter!')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('cleanup the resources.')
        return True

def with_t():
    with withclass() as wc:
        print('before exception!')
        raise Exception('A error occur!')
        print('after exception!')
    print('after with statement!')

if __name__ == '__main__':
    with_t()

輸出爲:

do enter!
before exception!
cleanup the resources.
after with statement!

可以看到拋出的異常被無視了,而資源也被成功釋放;原因是拋出異常後進入exit代碼並返回了True,而如果返回了False則會繼續拋出產生的異常,現在我們修改exit的返回爲False。

def __exit__(self, exc_type, exc_val, exc_tb):
        print('cleanup the resources.')
        return False

再看結果:

Traceback (most recent call last):
do enter!
  File "E:/QQSyncFolder/prj/mysite/pattern/with.py", line 30, in <module>
before exception!
    with_t()
cleanup the resources.
  File "E:/QQSyncFolder/prj/mysite/pattern/with.py", line 25, in with_t
    raise Exception('A error occur!')
Exception: A error occur!

可以發現,資源仍然被清除,但是Exception卻被拋出來,可以被外層程序捕獲處理。

總的而言,主要是爲了簡化try…except…finally的使用,同時使用with也避免了忘記在finally中釋放資源的情況。
像threading,mutliprocessing中Lock,Condition,Semaphore等多線程,多進程通訊都實現了context manager接口。

def RLock:    
    __enter__ = acquire
    def __exit__(self, t, v, tb):
        self.release()

# 下面的語法多開心:)

with rlock_instance:
    # do sth

PS: contextlib module 提供了許多簡單易用的context裝飾器,請參考Python官方文檔。

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