python學習 - 上下文管理,with語句

參考:
https://www.cnblogs.com/flashBoxer/p/9664813.html
https://blog.csdn.net/qq_37482956/article/details/100056517

gluon的一個例子:https://mxnet.incubator.apache.org/api/python/docs/_modules/mxnet/autograd.html#is_training # _RecordingStateScope 函數

with的語法
with EXPR as VAR:
    BLOCK

上面的語法的僞代碼

mgr = (EXPR)   
exit = type(mgr).__exit__  # Not calling it yet,還沒有執行
value = type(mgr).__enter__(mgr)    
exc = True     
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

對上面的僞代碼的一個解析:

  1. 生成上下文管理器mgr。
  2. 第2,3行會檢查是否有 __exit__ 和 __enter__,沒有的化,會拋出AttributeError異常。
  3. 將__enter__的值賦值給value;這個時候會調用__enter__並執行,__exit__還不會執行。
  4. 如果語法中沒有寫 as VAR,那麼 VAR = value 就被忽略。
  5. 執行BLOCK內的代碼:正常結束,或者是通過break, continue, return來結束;然後執行__exit__內的代碼,__exit__的三個參數都是None,__exit__參數分別是:異常類型,異常值,追溯信息。
  6. 如果執行出現異常,執行except中的語句,調用__exit__時的參數就是sys.exc_info() #(exc_type, exc_value, exc_traceback)

注意:如果 __enter__ 沒有返回值,那麼 as 對象獲取的值就是None

class WithTest:
    def __init__(self, filename):
        self.filename=filename
    def __enter__(self):
        self.f = open(self.filename, 'r')
       # return self.f
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

test = WithTest('file')

with test as t:
    print ('test result: {}'.format(t))

輸出結果:

test result: None

這個例子裏面__enter__沒有返回,所以with語句裏的"as t"到的是None,修改一下上面的例子:

class WithTest:
    def __init__(self, filename):
        self.filename=filename
    def __enter__(self):
        self.f = open(self.filename, 'r')
        return self.f
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

test = WithTest('file')

with test as t:
    print ('test result: {}'.format(t))

輸出:

test result: <_io.TextIOWrapper name='skb.kc' mode='r' encoding='cp936'>

__enter__函數裏有返回值賦值給as的對象t。

正常來講,在BLOCK發生的錯誤會被傳到__exit__的參數裏,如果BLOCK代碼沒有發生錯誤,那麼傳入的三個參數就None,如果發生了錯誤,那麼傳入__exit__的參數就是:錯誤類型,錯誤值,和追溯信息,並且__exit__返回值爲False的情況下才會觸發 raise,拋出異常信息。

因此,如果在__exit__中返回True,即使BLOCK代碼出現了錯誤,但仍然不會產生異常。

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