《擴展和嵌入python解釋器》1.2 Intermezzo: 錯誤和異常

1.2 Intermezzo: 錯誤和異常

下面是整個Python解釋器的一個重要慣例:當一個函數發生錯誤時,它應該設置一個異常狀態並返回一個錯誤值(通常是NULL指針)。異常存儲在解釋器內部的一個靜態的全局變量中;如果這個全局變量值爲NULL,表示沒有異常發生,第二個全局變量存儲異常(raise的第二個參數)的相關值。第三個變量存儲在Python代碼中發生錯誤時堆棧反向跟蹤數據。這三個變量是Python變量sys.exc_ type, sys.exc_value sys.exc_traceback(參看Python Library Referencesys模塊部分)對應的C變量。瞭解這些變量對於理解錯誤如何傳遞是非常重要的。

Python API定義了一些函數用來設置異常的各種類型。

最重要的一個是PyErr_SetString()。它的參數是一個異常對象和一個C字符串。異常對象通常是一個象PyExc_ZeroDivisionError一樣的預定義對象。C字符串指示錯誤的原因並被轉換爲Python字符串對象存儲在異常的相關值’associated value’)中。

另一個有用的函數是PyErr_SetFromErrno(),它只帶一個異常參數,並通過檢查全局變量errno來構造相關值。最常用的函數是PyErr_SetObject(),它帶兩個對象參數:異常和異常的相關值。你不必Py_INCREF()對象傳遞給這些函數。

檢測non-destructively檢測異常是否被PyErr_Occurred()函數設置。此函數[1]返回當前異常對象或在沒有異常產生時返回NULL。一般的,因爲你可以通過函數返回值識別錯誤,所以不必調用PyErr_Occurred()函數去查看是否在一個函數調用中有錯誤產生。

當函數F調用另一個函數G,並且檢測到後面的函數-G有錯誤,函數F應當返回一個錯誤值(通常爲NULL-1)。函數F應該不調用PyEr_*()函數之一 ――其中一個已經在G中調用。F的調用者必須也返回一個錯誤通知它的調用者,也不需要調用PyEr_*()函數,依次類推――最詳細的錯誤信息已經由最先探測到錯誤的函數報告了。一旦錯誤到達Python解釋器的主循環,這將終止當前執行的Python代碼,並試圖尋找Python程序員指定的異常句柄。

(在有些條件下,模塊能夠通過調用PyEr_*()函數給出確切的錯誤消息,並且在這種情況下,這麼做很好。然而,作爲一般規則,這是沒有必要的,並且會導致錯誤信息丟失:由於各種原因,大多數操作都是失敗的。)

爲了忽略由於函數調用失敗而產生的異常,異常條件必須通過明確調用函數PyErr_Clear()來清除。僅當不想傳遞錯誤給解釋器而想要完全自己處理錯誤時,C代碼才調用PyErr_Clear()函數。(可能是由於嘗試些別的東西,或裝做沒有錯誤發生)

每次調用malloc()失敗必須進入異常-malloc()直接調用者必須調用PyErr_NoMemory()並返回指示它自己錯誤的標誌。所有的對象創建函數(如:PyInt_FromLong())都已經這們做了,所以這條規則只和那些直接調用malloc()的函數相關。

還有一點,PyArg_ParseTuple()和相似函數產生的異常,帶有整數狀態返回值的函數都認爲返回值-1表示失敗,0或整數表示成功,就象UNIX系統調用一樣。

最後,當你返回一個錯誤指示時,小心地清除垃圾(通過調用你創建的對象的Py_XDECREF() Py_ DECREF()函數)。

產生哪個異常完全由你自己選擇。有一些預先聲明的C對象處理所有內置Python異常,如PyExc_ZeroDivisionError:你可以直接使用。當然,你應合理明智地選擇異常,不要使用PyExc_TypeError表示文件不能打開(而應該使用PyExc_IOError)。如果參數列表發生一些錯誤,函數PyArg_ParseTuple()通常產生PyExc_TypeError錯誤。如果你有的參數必須在特定範圍內,或必須滿足其他條件,應該使用PyExc_ValueError異常。

你也可以在自己的模塊中定義獨一無二的新異常。爲此,你應該在你的文件開始處聲明一個靜態的對象變量:

static PyObject *SpamError;

並且在你的模塊的初始化函數(initspam())中用一個異常對象初始化它(不考慮錯誤檢查)。

 

PyMODINIT_FUNC
initspam(void)
{
    PyObject *m;

    m = Py_InitModule("spam", SpamMethods);

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_INCREF(SpamError);
    PyModule_AddObject(m, "error", SpamError);
}

請注意:Python中異常的名稱爲spam.error.PyErr_NewException()函數可以從一個異常基類派生的新類(除非傳入另一個類而不是NULL),《Python Library Reference》內置異常(Built-in Exceptions)中有詳述。

 

還請注意:SpamError變量保持着新創建異常的引用;這是故意的!由於異常能夠被外部代碼從模塊中刪除,類的引用的擁有者必須確認引用對象沒有被釋放,導致SpamError成爲野指針。產生異常的C代碼一起內核轉儲或其他不可預料的後果。下面,我們將在這個例子中討論把PyMODINIT_FUN作爲函數的返回類型。


[1] 這個函數的返回值是這樣的嗎?

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