atexit — 程式關閉時回呼 — 你所不知道的 Python 標準函式庫用法 08

atexit – Exit handlers

The atexit module defines functions to register and unregister cleanup functions. Functions thus registered are automatically executed upon normal interpreter termination. atexit runs these functions in the reverse order in which they were registered; if you register AB, and C, at interpreter termination time they will be run in the order CBA.

官方介紹文件:29.8. atexit — Exit handlers

atexit 套件用來註冊當程式結束時要執行的函式。被 atexit 註冊的函式,會在程式正常結束的時機點被執行。執行的順序為先註冊後執行 (First Register Last Execute)。

01. Quickstart Tutorial

下面的程式碼會在程式結束時,印出函式的名稱:

執行它就會順序的印出 “registering”、”registered”、”cleanup_function”:

我們可以多次註冊同一個函式:

執行的結果:

我們註冊的順序是義大利文 (Ciao)、日文 (さようなら)、俄文 (Пока),在程式結束的時候,是以相反方向執行。你可以從這個例子發現,先註冊後執行這件事情。

你也可以使用 decorator 直接註冊一個函式:

執行的結果如下:

我們也可以透過 unregister 反註冊函式:

你可以多次執行反註冊在同一個函式上,或是反註冊一個沒有被註冊過的函式,這都沒有問題:

當函式被反註冊後,就會保證一定不會在程式結束時被執行。

02. HOW-TO Guides

什麼時候 atexit 註冊過的函式不會被執行?

好問題,以下三種情況不會執行被註冊過的函式:

  • 當殺死程式的 signal 不是由 Python handle 的時候
  • 當 Python 遇到嚴重的內部錯誤時
  • 當使用 os._exit() 的時候

以下就來驗證是否如此,下面驗證第一點:

我們現在來執行,透過鍵盤,我們可以發送 Ctrl + C (SIGINT) 以及 Ctrl+ \ (SIGQUIT) 到終端機上:

因為 Python 有 handle KeyboardInterrupt (Ctrl + C) 因此有印出 Goodbye, seeya。但是 SIGQUIT 沒有 handle,因此就直接退出而沒有印出 Goodbye, seeya。

接著來驗證第二點,我們故意弄出 Fatal Python Error (bpo-30953,fatal on 3.7a01, 3.6.2)

結果輸出如下:

因為產生 Fatal Python error,因此沒有印出 Goodbye, seeya。

最後驗證第三點 os._exit()

輸出如下:

不要懷疑,輸出是空的。

03. Discussions

atexit 模組在直到 Python 2.7 版本的時候都還是以純 Python code 寫成,直到 Python 3000 第一版 3.0 出來時,因為把 sys.exitfunc 從 sys module 中移除,才將 Lib/atexit.py 轉為以 C 撰寫的 Modules/atexitmodule.catexit 模組可以達成目標的原因,是由於 Python/pylifecycle.c 在其中提供了 _Py_PyAtExit() 這樣一組內部 API,提供 atexit 可以將 callback function 掛上去的機制。實際上在 finalize 的時候,會在 Py_FinalizeEx() 呼叫 call_py_exitfuncs() 這個函式,透過 call_py_exitfuncs() 去觸發剛剛使用 _Py_PyAtExit() 掛上的 callback functioin。

至於為什麼將 call_py_exitfuncs() 放在 Py_FinalizeEx() 這個函式中就可以在結束時呼叫到呢?這是因為 Py_FinalizeEx 被包在 Py_Exit() 裏,而 Py_Exit 就是正式的,當要結束時需要呼叫的 C-API。因此除了上面提到的三個狀況不會觸發 atexit 所註冊過的函式,就會在結束時觸發 call_py_exitfuncs 而將註冊的函式執行。

在標準函式庫中,前面所提到的 weakref.finalizeconcurrent.futures 等都有用到 atexit,可以說是對於這些有需要 finalize 工作的模組而言,atexit 可是居加必備良藥呢。

04. References

Leave a reply:

Your email address will not be published.