Python進階——什麼是上下文管理器?

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"閱讀本文大約需要 12 分鐘。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Python 開發中,我們經常會使用到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊,例如在讀寫文件時,保證文件描述符的正確關閉,避免資源泄露問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你有沒有思考過, ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 背後是如何實現的?我們常常聽到的上下文管理器究竟是什麼?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章我們就來學習一下 Python 上下文管理器,以及 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的運行原理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"with語法塊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在講解 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法之前,我們先來看一下不使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的代碼如何寫?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在操作一個文件時,代碼可以這麼寫:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 打開文件\nf = open('file.txt')\nfor line in f:\n # 讀取文件內容 執行其他操作\n # do_something...\n# 關閉文件\nf.close()","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個例子非常簡單,就是打開一個文件,然後讀取文件中的內容,最後關閉文件釋放資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,代碼這麼寫會有一個問題:在打開文件後,如果要對讀取到的內容進行其他操作,在這操作期間發生了異常,這就會導致文件句柄無法被釋放,進而導致資源的泄露。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何解決這個問題?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也很簡單,我們使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"try ... finally","attrs":{}}],"attrs":{}},{"type":"text","text":" 來優化代碼:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# 打開文件\nf = open('file.txt')\ntry:\n for line in f:\n # 讀取文件內容 執行其他操作\n # do_something...\nfinally:\n # 保證關閉文件\n f.close()","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這麼寫的好處是,在讀取文件內容和操作期間,無論是否發生異常,都可以保證最後能釋放文件資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但這麼優化,代碼結構會變得很繁瑣,每次都要給代碼邏輯增加 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"try ... finally","attrs":{}}],"attrs":{}},{"type":"text","text":" 纔可以,可讀性變得很差。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對這種情況,我們就可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊來解決這個問題:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"with open('file.txt') as f:\n for line in f:\n # do_something...","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊可以完成之前相同的功能,而且這麼寫的好處是,代碼結構變得非常清晰,可讀性也很好。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明白了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的作用,那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 究竟是如何運行的呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"上下文管理器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們來看一下 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的語法格式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"with context_expression [as target(s)]:\n with-body","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法非常簡單,我們只需要 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 一個表達式,然後就可以執行自定義的業務邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 後面的表達式是可以任意寫的嗎?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是否定的。要想使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 後面的的對象需要實現「上下文管理器協議」。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"什麼是「上下文管理器協議」?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個類在 Python 中,只要實現以下方法,就實現了「上下文管理器協議」:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":":在進入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊之前調用,返回值會賦值給 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"target","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":":在退出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊時調用,一般用作異常處理","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看實現了這 2 個方法的例子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"class TestContext:\n\n def __enter__(self):\n print('__enter__')\n return 1\n\n def __exit__(self, exc_type, exc_value, exc_tb):\n print('exc_type: %s' % exc_type)\n print('exc_value: %s' % exc_value)\n print('exc_tb: %s' % exc_tb)\n\nwith TestContext() as t:\n print('t: %s' % t)\n \n# Output:\n# __enter__\n# t: 1\n# exc_type: None\n# exc_value: None\n# exc_tb: None","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個例子中,我們定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TestContext","attrs":{}}],"attrs":{}},{"type":"text","text":" 類,它分別實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"exit","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣一來,我們就可以把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TestContext","attrs":{}}],"attrs":{}},{"type":"text","text":" 當做一個「上下文管理器」來使用,也就是通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with TestContext() as t","attrs":{}}],"attrs":{}},{"type":"text","text":" 方式來執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從輸出結果我們可以看到,具體的執行流程如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在進入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊之前被調用,這個方法的返回值賦給了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 後的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"t","attrs":{}}],"attrs":{}},{"type":"text","text":" 變量","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 在執行完 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊之後被調用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊內發生了異常,那麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法可以拿到關於異常的詳細信息:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"exc_type","attrs":{}}],"attrs":{}},{"type":"text","text":":異常類型","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"exc_value","attrs":{}}],"attrs":{}},{"type":"text","text":":異常對象","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"exc_tb","attrs":{}}],"attrs":{}},{"type":"text","text":":異常堆棧信息","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看一個發生異常的例子,觀察 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法拿到的異常信息是怎樣的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"with TestContext() as t:\n # 這裏會發生異常\n a = 1 / 0 \n print('t: %s' % t)\n\n# Output:\n# __enter__\n# exc_type: \n# exc_value: integer division or modulo by zero\n# exc_tb: \n# Traceback (most recent call last):\n# File \"base.py\", line 16, in \n# a = 1 / 0\n# ZeroDivisionError: integer division or modulo by zero","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從輸出結果我們可以看到,當 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊內發生異常後,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 輸出了這個異常的詳細信息,其中包括異常類型、異常對象、異常堆棧。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們需要對異常做特殊處理,就可以在這個方法中實現自定義邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回到最開始我們講的,使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 讀取文件的例子。之所以 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 能夠自動關閉文件資源,就是因爲內置的文件對象實現了「上下文管理器協議」,這個文件對象的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法返回了文件句柄,並且在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 中實現了文件資源的關閉,另外,當 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊內有異常發生時,會拋出異常給調用者。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僞代碼可以這麼寫:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"class File:\n\n def __enter__(self):\n return file_obj\n\n def __exit__(self, exc_type, exc_value, exc_tb):\n # with 退出時釋放文件資源\n file_obj.close()\n # 如果 with 內有異常發生 拋出異常\n if exc_type is not None:\n raise exception","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏我們小結一下,通過對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的學習,我們瞭解到,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 非常適合用需要對於上下文處理的場景,例如操作文件、Socket,這些場景都需要在執行完業務邏輯後,釋放資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"contextlib模塊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於需要上下文管理的場景,除了自己實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 之外,還有更簡單的方式來做嗎?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是肯定的。我們可以使用 Python 標準庫提供的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊,來簡化我們的代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊,我們可以把上下文管理器當成一個「裝飾器」來使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊提供了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我們通過例子來看一下它們是如何使用的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"contextmanager裝飾器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器的使用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"from contextlib import contextmanager\n\n@contextmanager\ndef test():\n print('before')\n yield 'hello'\n print('after')\n\nwith test() as t:\n print(t)\n\n# Output:\n# before\n# hello\n# after","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個例子中,我們使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":"配合,實現了和前面上下文管理器相同的功能,它的執行流程如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"test()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,先打印出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"before","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield 'hello'","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"test","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法返回,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"hello","attrs":{}}],"attrs":{}},{"type":"text","text":" 返回值會賦值給 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"t","attrs":{}}],"attrs":{}},{"type":"text","text":" 變量","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊內的邏輯,打印出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"t","attrs":{}}],"attrs":{}},{"type":"text","text":" 的值 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"hello","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"又回到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"test","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法中,執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":" 後面的邏輯,打印出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"after","attrs":{}}],"attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣一來,當我們使用這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器後,就不用再寫一個類來實現上下文管理協議,只需要用一個方法裝飾對應的方法,就可以實現相同的功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過有一點需要我們注意:在使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器時,**如果被裝飾的方法內發生了異常,那麼我們需要在自己的方法中進行異常處理,否則將不會執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":" 之後的邏輯。**","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"@contextmanager\ndef test():\n print('before')\n try:\n yield 'hello'\n # 這裏發生異常 必須自己處理異常邏輯 否則不會向下執行\n a = 1 / 0 \n finally:\n print('after')\n\nwith test() as t:\n print(t)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"closing方法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們再來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 提供的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法如何使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 主要用在已經實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"close","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的資源對象上:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"from contextlib import closing\n\nclass Test():\n\n # 定義了 close 方法纔可以使用 closing 裝飾器\n def close(self):\n print('closed')\n\n# with 塊執行結束後 自動執行 close 方法\nwith closing(Test()):\n print('do something')\n \n# Output:\n# do something\n# closed","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從執行結果我們可以看到,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語句塊執行結束後,會自動調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Test","attrs":{}}],"attrs":{}},{"type":"text","text":" 實例的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"close","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,對於需要自定義關閉資源的場景,我們可以使用這個方法配合 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 來完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"contextlib的實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"學習完了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊的使用,最後我們來看一下 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊是究竟是如何實現的?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊相關的源碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"class _GeneratorContextManagerBase:\n\n def __init__(self, func, args, kwds):\n # 接收一個生成器對象 (方法內包含 yield 的方法就是一個生成器)\n self.gen = func(*args, **kwds)\n self.func, self.args, self.kwds = func, args, kwds\n doc = getattr(func, \"__doc__\", None)\n if doc is None:\n doc = type(self).__doc__\n self.__doc__ = doc\n\nclass _GeneratorContextManager(_GeneratorContextManagerBase,\n AbstractContextManager,\n ContextDecorator):\n\n def __enter__(self):\n try:\n # 執行生成器 代碼會運行生成器方法的 yield 處\n return next(self.gen)\n except StopIteration:\n raise RuntimeError(\"generator didn't yield\") from None\n\n def __exit__(self, type, value, traceback):\n # with 內沒有異常發生\n if type is None:\n try:\n # 繼續執行生成器\n next(self.gen)\n except StopIteration:\n return False\n else:\n raise RuntimeError(\"generator didn't stop\")\n # with 內發生了異常\n else:\n if value is None:\n value = type()\n try:\n # 拋出異常\n self.gen.throw(type, value, traceback)\n except StopIteration as exc:\n return exc is not value\n except RuntimeError as exc:\n if exc is value:\n return False\n if type is StopIteration and exc.__cause__ is value:\n return False\n raise\n except:\n if sys.exc_info()[1] is value:\n return False\n raise\n raise RuntimeError(\"generator didn't stop after throw()\")\n\ndef contextmanager(func):\n @wraps(func)\n def helper(*args, **kwds):\n return _GeneratorContextManager(func, args, kwds)\n return helper\n\nclass closing(AbstractContextManager):\n def __init__(self, thing):\n self.thing = thing\n def __enter__(self):\n return self.thing\n def __exit__(self, *exc_info):\n self.thing.close()","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源碼中我已經添加好了註釋,你可以詳細看一下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 源碼中邏輯其實比較簡單,其中 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器實現邏輯如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"初始化一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_GeneratorContextManager","attrs":{}}],"attrs":{}},{"type":"text","text":" 類,構造方法接受了一個生成器 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"gen","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"這個類實現了上下文管理器協議 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 時會進入到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,然後執行這個生成器,執行時會運行到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊內的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":" 處","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 返回 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":" 的結果","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"如果 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊沒有發生異常,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 執行結束後,會進入到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"exit","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,再次執行生成器,這時會運行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":" 之後的代碼邏輯","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"如果 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊發生了異常,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 會把這個異常通過生成器,傳入到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊內,也就是把異常拋給調用者","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再來看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法就是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__exit__","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法中調用了自定義對象的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"close","attrs":{}}],"attrs":{}},{"type":"text","text":",這樣當 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 結束後就會執行我們定義的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"close","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"使用場景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"學習完了上下文管理器,那麼它們具體會用在什麼場景呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我舉幾個常用的例子來演示下,你可以參考一下結合自己的場景使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Redis分佈式鎖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"from contextlib import contextmanager\n\n@contextmanager\ndef lock(redis, lock_key, expire):\n try:\n locked = redis.set(lock_key, 'locked', expire)\n yield locked\n finally:\n redis.delete(lock_key)\n\n# 業務調用 with 代碼塊執行結束後 自動釋放鎖資源\nwith lock(redis, 'locked', 3) as locked:\n if not locked:\n return\n # do something ...","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個例子中,我們實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lock","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,用於在 Redis 上申請一個分佈式鎖,然後使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器裝飾了這個方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後我們業務在調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lock","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法時,就可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊的第一步,首先判斷是否申請到了分佈式鎖,如果申請失敗,則業務邏輯直接返回。如果申請成功,則執行具體的業務邏輯,當業務邏輯執行完成後,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 退出時會自動釋放分佈式鎖,就不需要我們每次都手動釋放鎖了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Redis事物和管道","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"from contextlib import contextmanager\n\n@contextmanager\ndef pipeline(redis):\n pipe = redis.pipeline()\n try:\n yield pipe\n pipe.execute()\n except Exception as exc:\n pipe.reset()\n \n# 業務調用 with 代碼塊執行結束後 自動執行 execute 方法\nwith pipeline(redis) as pipe:\n pipe.set('key1', 'a', 30)\n pipe.zadd('key2', 'a', 1)\n pipe.sadd('key3', 'a')","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個例子中,我們定義了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pipeline","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,並使用裝飾器 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 讓它變成了一個上下文管理器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後在調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with pipeline(redis) as pipe","attrs":{}}],"attrs":{}},{"type":"text","text":" 時,就可以開啓一個事物和管道,然後在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊內向這個管道中添加命令,最後 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 退出時會自動執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pipeline","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"execute","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,把這些命令批量發送給 Redis 服務端。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在執行命令時發生了異常,則會自動調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pipeline","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"reset","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,放棄這個事物的執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結一下,這篇文章我們主要介紹了 Python 上下文管理器的使用及實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我們介紹了不使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 和使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 操作文件的代碼差異,然後瞭解到使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 可以讓我們的代碼結構更加簡潔。之後我們探究了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現原理,只要實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"__enter__","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"exit","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的實例,就可以配合 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"with","attrs":{}}],"attrs":{}},{"type":"text","text":" 語法塊來使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後我們介紹了 Python 標準庫的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextlib","attrs":{}}],"attrs":{}},{"type":"text","text":" 模塊,它提供了實現上下文管理更好的使用方式,我們可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"contextmanager","attrs":{}}],"attrs":{}},{"type":"text","text":" 裝飾器和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"closing","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法來操作我們的資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我舉了兩個例子,來演示上下文管理器的具體使用場景,例如在 Redis 中使用分佈式鎖和事物管道,用上下文管理器幫我們管理資源,執行前置和後置邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,如果我們在開發中把操作資源的前置和後置邏輯,通過上下文管理器來實現,那麼我們的代碼結構和可維護性也會有所提高,推薦使用起來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c10b027be63b0c5c1ffc36a76a4a974b.png","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"關注「水滴與銀彈」公衆號,7年資深後端研發,和你分享更多優質技術乾貨。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章