理解Python中的“with”

source 8年前發布 | 21K 次閱讀 Python Python開發

 

1. 緣起

Python中,打開文件的操作是非常常見的,也是非常方便的,那么如何優雅的打開一個文件?大部分的同學會這樣實現:

with open( "a.txt" ) as f :
    # do something

大家都知道,這樣寫可以自動處理資源的釋放、處理異常等,化簡了我們打開文件的操作,那么, with 到底做了什么呢?

從《Python學習手冊》中是這么描述的:

簡而言之,with/as語句的設計是作為常見try/finally用法模式的替代方案。就像try/finally語句,with/as語句也是用于定義必須執行的終止或“清理”行為,無論步驟中是否發生異常。不過,和try/finally不同的是,with語句支持更豐富的基于對象的協議,可以為代碼塊定義支持進入和離開動作。

也就是說對于代碼:

with expression [as varible]:
    with-block

with語句的實際工作方式:

1.計算表達式,所得到的對象是 環境管理器 ,他必須有 enterexit 兩個方法。

2.環境管理器的 enter 方法會被調用。如果as存在, enter 的返回值賦值給as后面的變量,否則,被丟棄。

3.代碼塊中嵌套的代碼(with-block)會執行。

4.如果with代碼塊會引發異常, exit (type,value,traceback)方法就會被調用。這些也是由sys.exec

info返回相同的值。如果此方法返回為假,則異常會重新引發。否則,異常會中止。正常情況下異常是應該被重新引發,這樣的話傳遞到with語句外。

 

5.如果with代碼塊沒有引發異常, _exit

方法依然會調用,其type、value以及traceback參數會以None傳遞。

with/as語句的設計,是為了讓必須在程序代碼塊周圍發生的啟動和終止活動一定會發生。和try/finally語句(無論異常是否發生其離開動作都會執行)類似,但是with/as有更豐富的對象協議,可以定義進入和離開的動作。

2. 設計的初衷

with/as語句的設計的初衷,在 PEP343 中是這么描述的:

This PEP adds a new statement “with” to the Python language to make it possible to factor out standard uses of try/finally statements.

In this PEP, context managers provide enter () and exit () methods that are invoked on entry to and exit from the body of the with statement.

對于下面的操作:

with EXPR as VAR:
            BLOCK

等價于

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        # 將__enter__函數調用的返回值返回給VAR
        VAR = value  # Only if "as VAR" is present
        # 執行BLOCK
        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)

我們可以看到上述代碼完整的處理了初始化及異常/正常場景的清理操作,這便是 with 的設計思想,化簡了冗余的代碼,把那些重復的工作以及異常處理操作交給寫“EXPR”源碼(比如open操作)的同學。

3. 更深入的學習

我們繼續深入的看下 Python3enterexit 的實現:

class IOBase(metaclass=abc.ABCMeta):
    # ... ...

    ### Context manager ###

    def __enter__(self):  # That's a forward reference
        """Context management protocol. Returns self (an instance of IOBase)."""
        self._checkClosed()
        return self

    def __exit__(self, *args):
        """Context management protocol. Calls close()"""
        self.close()

和我們預期的一致,在 enter 中返回了這個IO對象,然后在 exit 中,進行了清理。

參考資料

  1. 《Python學習手冊》
  2. Understanding Python’s “with” statement
  3. PEP 343 — The “with” Statement
  4. Catching an exception while using a Python ‘with’ statement
  5. 理解Python中的with…as…語法
  6. PEP 3116 — New I/O
  7. Python 3.5.0 Code

來自: http://yikun.github.io/2016/04/15/理解Python中的“with”/ 

 本文由用戶 source 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!