新聞中心
概述
python 的應(yīng)用編程接口(API)使得 C 和 C++ 程序員可以在多個(gè)層級(jí)上訪問 Python 解釋器。該 API 在 C++ 中同樣可用,但為簡(jiǎn)化描述,通常將其稱為 Python/C API。使用 Python/C API 有兩個(gè)基本的理由。第一個(gè)理由是為了特定目的而編寫 擴(kuò)展模塊;它們是擴(kuò)展 Python 解釋器功能的 C 模塊。這可能是最常見的使用場(chǎng)景。第二個(gè)理由是將 Python 用作更大規(guī)模應(yīng)用的組件;這種技巧通常被稱為在一個(gè)應(yīng)用中 embedding Python。

成都創(chuàng)新互聯(lián)公司2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站制作、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元館陶做網(wǎng)站,已為上家服務(wù),為館陶各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
編寫擴(kuò)展模塊的過程相對(duì)來說更易于理解,可以通過“菜譜”的形式分步驟介紹。使用某些工具可在一定程度上自動(dòng)化這一過程。雖然人們?cè)谄渌麘?yīng)用中嵌入 Python 的做法早已有之,但嵌入 Python 的過程沒有編寫擴(kuò)展模塊那樣方便直觀。
許多 API 函數(shù)在你嵌入或是擴(kuò)展 Python 這兩種場(chǎng)景下都能發(fā)揮作用;此外,大多數(shù)嵌入 Python 的應(yīng)用程序也需要提供自定義擴(kuò)展,因此在嘗試在實(shí)際應(yīng)用中嵌入 Python 之前先熟悉編寫擴(kuò)展應(yīng)該會(huì)是個(gè)好主意。
代碼標(biāo)準(zhǔn)
如果你想要編寫可包含于 CPython 的 C 代碼,你 必須 遵循在 PEP 7 中定義的指導(dǎo)原則和標(biāo)準(zhǔn)。這些指導(dǎo)原則適用于任何你所要擴(kuò)展的 Python 版本。在編寫你自己的第三方擴(kuò)展模塊時(shí)可以不必遵循這些規(guī)范,除非你準(zhǔn)備在日后向 Python 貢獻(xiàn)這些模塊。
包含文件
使用 Python/C API 所需要的全部函數(shù)、類型和宏定義可通過下面這行語句包含到你的代碼之中:
#define PY_SSIZE_T_CLEAN#include
這意味著包含以下標(biāo)準(zhǔn)頭文件:,,,, 和 (如果可用)。
備注
由于 Python 可能會(huì)定義一些能在某些系統(tǒng)上影響標(biāo)準(zhǔn)頭文件的預(yù)處理器定義,因此在包含任何標(biāo)準(zhǔn)頭文件之前,你 必須 先包含 Python.h。
推薦總是在 Python.h 前定義 PY_SSIZE_T_CLEAN 。查看 解析參數(shù)并構(gòu)建值變量 來了解這個(gè)宏的更多內(nèi)容。
Python.h 所定義的全部用戶可見名稱(由包含的標(biāo)準(zhǔn)頭文件所定義的除外)都帶有前綴 Py 或者 _Py。以 _Py 打頭的名稱是供 Python 實(shí)現(xiàn)內(nèi)部使用的,不應(yīng)被擴(kuò)展編寫者使用。結(jié)構(gòu)成員名稱沒有保留前綴。
備注
用戶代碼永遠(yuǎn)不應(yīng)該定義以 Py 或 _Py 開頭的名稱。這會(huì)使讀者感到困惑,并危及用戶代碼對(duì)未來Python版本的可移植性,這些版本可能會(huì)定義以這些前綴之一開頭的其他名稱。
頭文件通常會(huì)與 Python 一起安裝。在 Unix 上,它們位于以下目錄:*prefix*/include/pythonversion/ 和 *exec_prefix*/include/pythonversion/,其中 prefix 和 exec_prefix 是由向 Python 的 configure 腳本傳入的對(duì)應(yīng)形參所定義,而 version 則為 '%d.%d' % sys.version_info[:2]。在 Windows 上,頭文件安裝于 *prefix*/include,其中 prefix 是向安裝程序指定的安裝目錄。
要包含頭文件,請(qǐng)將兩個(gè)目錄(如果不同)都放到你所用編譯器的包含搜索路徑中。請(qǐng) 不要 將父目錄放入搜索路徑然后使用 #include ;這將使得多平臺(tái)編譯不可用,因?yàn)?prefix 下平臺(tái)無關(guān)的頭文件需要包含來自 exec_prefix 下特定平臺(tái)的頭文件。
C++ 用戶應(yīng)該注意,盡管 API 是完全使用 C 來定義的,但頭文件正確地將入口點(diǎn)聲明為 extern "C",因此 API 在 C++ 中使用此 API 不必再做任何特殊處理。
有用的宏
Python 頭文件中定義了一些有用的宏。許多是在靠近它們被使用的地方定義的(例如 Py_RETURN_NONE)。其他更為通用的則定義在這里。這里所顯示的并不是一個(gè)完整的列表。
Py_ABS(x)
返回 x 的絕對(duì)值。
3.3 新版功能.
Py_ALWAYS_INLINE
Ask the compiler to always inline a static inline function. The compiler can ignore it and decides to not inline the function.
It can be used to inline performance critical static inline functions when building Python in debug mode with function inlining disabled. For example, MSC disables function inlining when building in debug mode.
Marking blindly a static inline function with Py_ALWAYS_INLINE can result in worse performances (due to increased code size for example). The compiler is usually smarter than the developer for the cost/benefit analysis.
If Python is built in debug mode (if the Py_DEBUG macro is defined), the Py_ALWAYS_INLINE macro does nothing.
It must be specified before the function return type. Usage:
static inline Py_ALWAYS_INLINE int random(void) { return 4; }
3.11 新版功能.
Py_CHARMASK(c)
參數(shù)必須為 [-128, 127] 或 [0, 255] 范圍內(nèi)的字符或整數(shù)類型。這個(gè)宏將 c 強(qiáng)制轉(zhuǎn)換為 unsigned char 返回。
Py_DEPRECATED(version)
棄用聲明。該宏必須放置在符號(hào)名稱前。
示例:
Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);
在 3.8 版更改: 添加了 MSVC 支持。
Py_GETENV(s)
與 getenv(s) 類似,但是如果命令行上傳遞了 -E ,則返回 NULL (即如果設(shè)置了 Py_IgnoreEnvironmentFlag )。
Py_MAX(x, y)
返回 x 和 y 當(dāng)中的最大值。
3.3 新版功能.
Py_MEMBER_SIZE(type, member)
返回結(jié)構(gòu) (type) member 的大小,以字節(jié)表示。
3.6 新版功能.
Py_MIN(x, y)
返回 x 和 y 當(dāng)中的最小值。
3.3 新版功能.
Py_NO_INLINE
Disable inlining on a function. For example, it reduces the C stack consumption: useful on LTO+PGO builds which heavily inline code (see bpo-33720).
Usage:
Py_NO_INLINE static int random(void) { return 4; }
3.11 新版功能.
Py_STRINGIFY(x)
將 x 轉(zhuǎn)換為 C 字符串。例如 Py_STRINGIFY(123) 返回 "123"。
3.4 新版功能.
Py_UNREACHABLE()
這個(gè)可以在你有一個(gè)設(shè)計(jì)上無法到達(dá)的代碼路徑時(shí)使用。例如,當(dāng)一個(gè) switch 語句中所有可能的值都已被 case 子句覆蓋了,就可將其用在 default: 子句中。當(dāng)你非常想在某個(gè)位置放一個(gè) assert(0) 或 abort() 調(diào)用時(shí)也可以用這個(gè)。
在 release 模式下,該宏幫助編譯器優(yōu)化代碼,并避免發(fā)出不可到達(dá)代碼的警告。例如,在 GCC 的 release 模式下,該宏使用 __builtin_unreachable() 實(shí)現(xiàn)。
Py_UNREACHABLE() 的一個(gè)用法是調(diào)用一個(gè)不會(huì)返回,但卻沒有聲明 _Py_NO_RETURN 的函數(shù)之后。
如果一個(gè)代碼路徑不太可能是正常代碼,但在特殊情況下可以到達(dá),就不能使用該宏。例如,在低內(nèi)存條件下,或者一個(gè)系統(tǒng)調(diào)用返回超出預(yù)期范圍值,諸如此類,最好將錯(cuò)誤報(bào)告給調(diào)用者。如果無法將錯(cuò)誤報(bào)告給調(diào)用者,可以使用 Py_FatalError() 。
3.7 新版功能.
Py_UNUSED(arg)
用于函數(shù)定義中未使用的參數(shù),從而消除編譯器警告。例如: int func(int a, int Py_UNUSED(b)) { return a; } 。
3.4 新版功能.
PyDoc_STRVAR(name, str)
創(chuàng)建一個(gè)可以在文檔字符串中使用的,名字為 name 的變量。如果不和文檔字符串一起構(gòu)建 Python,該值將為空。
如 PEP 7 所述,使用 PyDoc_STRVAR 作為文檔字符串,以支持不和文檔字符串一起構(gòu)建 Python 的情況。
示例:
PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element.");static PyMethodDef deque_methods[] = {// ...{"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc},// ...}
PyDoc_STR(str)
為給定的字符串輸入創(chuàng)建一個(gè)文檔字符串,或者當(dāng)文檔字符串被禁用時(shí),創(chuàng)建一個(gè)空字符串。
如 PEP 7 所述,使用 PyDoc_STR 指定文檔字符串,以支持不和文檔字符串一起構(gòu)建 Python 的情況。
示例:
static PyMethodDef pysqlite_row_methods[] = {{"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS,PyDoc_STR("Returns the keys of the row.")},{NULL, NULL}};
對(duì)象、類型和引用計(jì)數(shù)
Most Python/C API functions have one or more arguments as well as a return value of type PyObject*. This type is a pointer to an opaque data type representing an arbitrary Python object. Since all Python object types are treated the same way by the Python language in most situations (e.g., assignments, scope rules, and argument passing), it is only fitting that they should be represented by a single C type. Almost all Python objects live on the heap: you never declare an automatic or static variable of type PyObject, only pointer variables of type PyObject* can be declared. The sole exception are the type objects; since these must never be deallocated, they are typically static PyTypeObject objects.
所有 Python 對(duì)象(甚至 Python 整數(shù))都有一個(gè) type 和一個(gè) reference count。對(duì)象的類型確定它是什么類型的對(duì)象(例如整數(shù)、列表或用戶定義函數(shù);還有更多,如 標(biāo)準(zhǔn)類型層級(jí)結(jié)構(gòu) 中所述)。對(duì)于每個(gè)眾所周知的類型,都有一個(gè)宏來檢查對(duì)象是否屬于該類型;例如,當(dāng)(且僅當(dāng)) a 所指的對(duì)象是 Python 列表時(shí) PyList_Check(a) 為真。
引用計(jì)數(shù)
引用計(jì)數(shù)非常重要,因?yàn)楝F(xiàn)代計(jì)算機(jī)內(nèi)存(通常十分)有限;它計(jì)算有多少不同的地方引用同一個(gè)對(duì)象。這樣的地方可以是某個(gè)對(duì)象,或者是某個(gè)全局(或靜態(tài))C 變量,亦或是某個(gè) C 函數(shù)的局部變量。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)變?yōu)?0,釋放該對(duì)象。如果這個(gè)已釋放的對(duì)象包含其它對(duì)象的引用計(jì)數(shù),則遞減這些對(duì)象的引用計(jì)數(shù)。如果這些對(duì)象的引用計(jì)數(shù)減少為零,則可以依次釋放這些對(duì)象,依此類推。(這里有一個(gè)很明顯的問題——對(duì)象之間相互引用;目前,解決方案是“不要那樣做”。)
總是顯式操作引用計(jì)數(shù)。通常的方法是使用宏 Py_INCREF() 來增加一個(gè)對(duì)象的引用計(jì)數(shù),使用宏 Py_DECREF() 來減少一個(gè)對(duì)象的引用計(jì)數(shù)。宏 Py_DECREF() 必須檢查引用計(jì)數(shù)是否為零,然后調(diào)用對(duì)象的釋放器, 因此它比 incref 宏復(fù)雜得多。釋放器是一個(gè)包含在對(duì)象類型結(jié)構(gòu)中的函數(shù)指針。如果對(duì)象是復(fù)合對(duì)象類型(例如列表),則類型特定的釋放器負(fù)責(zé)遞減包含在對(duì)象中的其他對(duì)象的引用計(jì)數(shù),并執(zhí)行所需的終結(jié)。引用計(jì)數(shù)不會(huì)溢出,至少用與虛擬內(nèi)存中不同內(nèi)存位置一樣多的位用于保存引用計(jì)數(shù)(即 sizeof(Py_ssize_t) >= sizeof(void*) )。因此,引用計(jì)數(shù)遞增是一個(gè)簡(jiǎn)單的操作。
沒有必要為每個(gè)包含指向?qū)ο蟮闹羔樀木植孔兞吭黾訉?duì)象的引用計(jì)數(shù)。理論上,當(dāng)變量指向?qū)ο髸r(shí),對(duì)象的引用計(jì)數(shù)增加 1 ,當(dāng)變量超出范圍時(shí),對(duì)象的引用計(jì)數(shù)減少 1 。但是,這兩者相互抵消,所以最后引用計(jì)數(shù)沒有改變。使用引用計(jì)數(shù)的唯一真正原因是只要我們的變量指向它,就可以防止對(duì)象被釋放。如果知道至少有一個(gè)對(duì)該對(duì)象的其他引用存活時(shí)間至少和我們的變量一樣長(zhǎng),則沒必要臨時(shí)增加引用計(jì)數(shù)。一個(gè)典型的情形是,對(duì)象作為參數(shù)從 Python 中傳遞給被調(diào)用的擴(kuò)展模塊中的 C 函數(shù)時(shí),調(diào)用機(jī)制會(huì)保證在調(diào)用期間持有對(duì)所有參數(shù)的引用。
但是,有一個(gè)常見的陷阱是從列表中提取一個(gè)對(duì)象,并將其持有一段時(shí)間,而不增加其引用計(jì)數(shù)。某些操作可能會(huì)從列表中刪除某個(gè)對(duì)象,減少其引用計(jì)數(shù),并有可能重新分配這個(gè)對(duì)象。真正的危險(xiǎn)是,這個(gè)看似無害的操作可能會(huì)調(diào)用任意 Python 代碼——也許有一個(gè)代碼路徑允許控制流從 Py_DECREF() 回到用戶,因此在復(fù)合對(duì)象上的操作都存在潛在的風(fēng)險(xiǎn)。
一個(gè)安全的方式是始終使用泛型操作(名稱以 PyObject_ , PyNumber_ , PySequence_ 或 PyMapping_ 開頭的函數(shù))。這些操作總是增加它們返回的對(duì)象的引用計(jì)數(shù)。這讓調(diào)用者有責(zé)任在獲得結(jié)果后調(diào)用 Py_DECREF() 。習(xí)慣這種方式很簡(jiǎn)單。
引用計(jì)數(shù)細(xì)節(jié)
Python/C API 中函數(shù)引用計(jì)數(shù)行為最好是通過 引用所有權(quán) 來解釋。 所有權(quán)是關(guān)聯(lián)到引用,而不是對(duì)象(對(duì)象沒有所有權(quán):它們總是會(huì)被共享)。 “獲得引用所有權(quán)”意味著當(dāng)不再需要該引用時(shí)必須在其上調(diào)用 Py_DECREF。 所有權(quán)也可以被轉(zhuǎn)移,這意味著接受該引用所有權(quán)的代碼當(dāng)不再需要該引用時(shí)必須通過調(diào)用 Py_DECREF() 或 Py_XDECREF() 來最終撤銷引用 —- 或是將這個(gè)責(zé)任轉(zhuǎn)移出去(通常是轉(zhuǎn)給其調(diào)用方)。 當(dāng)一個(gè)函數(shù)將引用所有權(quán)轉(zhuǎn)給其調(diào)用方時(shí),則稱調(diào)用方收到了一個(gè) 新的 引用。 當(dāng)所有權(quán)未被轉(zhuǎn)移時(shí),則稱調(diào)用方 借入 該引用。 對(duì)于 borrowed reference 來說不需任何額外操作。
相反地,當(dāng)調(diào)用方函數(shù)傳入一個(gè)對(duì)象的引用時(shí),存在兩種可能:該函數(shù) 竊取 了一個(gè)對(duì)象的引用,或是沒有竊取。 竊取引用 意味著當(dāng)你向一個(gè)函數(shù)傳入引用時(shí),該函數(shù)會(huì)假定它擁有該引用,而你將不再對(duì)它負(fù)有責(zé)任。
很少有函數(shù)會(huì)竊取引用;兩個(gè)重要的例外是 PyList_SetItem() 和 PyTuple_SetItem(),它們會(huì)竊取對(duì)條目的引用(但不是條目所在的元組或列表!)。 這些函數(shù)被設(shè)計(jì)為會(huì)竊取引用是因?yàn)樵谑褂眯聞?chuàng)建的對(duì)象來填充元組或列表時(shí)有一個(gè)通常的慣例;例如,創(chuàng)建元組 (1, 2, "three") 的代碼看起來可以是這樣的(暫時(shí)不要管錯(cuò)誤處理;下面會(huì)顯示更好的代碼編寫方式):
PyObject *t;t = PyTuple_New(3);PyTuple_SetItem(t, 0, PyLong_FromLong(1L));PyTuple_SetItem(t, 1, PyLong_FromLong(2L));PyTuple_SetItem(t, 2, PyUnicode_FromString("three"));
在這里,PyLong_FromLong() 返回了一個(gè)新的引用并且它立即被 PyTuple_SetItem() 所竊取。 當(dāng)你想要繼續(xù)使用一個(gè)對(duì)象而對(duì)它的引用將被竊取時(shí),請(qǐng)?jiān)谡{(diào)用竊取引用的函數(shù)之前使用 Py_INCREF() 來抓取另一個(gè)引用。
順便提一下,PyTuple_SetItem() 是設(shè)置元組條目的 唯一 方式;PySequence_SetItem() 和 PyObject_SetItem() 會(huì)拒絕這樣做因?yàn)樵M是不可變數(shù)據(jù)類型。 你應(yīng)當(dāng)只對(duì)你自己創(chuàng)建的元組使用 PyTuple_SetItem()。
等價(jià)于填充一個(gè)列表的代碼可以使用 PyList_New() 和 PyList_SetItem() 來編寫。
然而,在實(shí)踐中,你很少會(huì)使用這些創(chuàng)建和填充元組或列表的方式。 有一個(gè)通用的函數(shù) Py_BuildValue() 可以根據(jù) C 值來創(chuàng)建大多數(shù)常用對(duì)象,由一個(gè) 格式字符串 來指明。 例如,上面的兩個(gè)代碼塊可以用下面的代碼來代替(還會(huì)負(fù)責(zé)錯(cuò)誤檢測(cè)):
PyObject *tuple, *list;tuple = Py_BuildValue("(iis)", 1, 2, "three");list = Py_BuildValue("[iis]", 1, 2, "three");
使用 PyObject_SetItem() 等來處理那些你只是借入引用的條目是更為常見的,例如傳給你正在編寫的函數(shù)的參數(shù)。 在這種情況下,他們對(duì)于引用計(jì)數(shù)的行為會(huì)更為理智,因?yàn)槟悴恍枰f增引用計(jì)數(shù)以便你可以將引用計(jì)數(shù)轉(zhuǎn)出去(“讓它被竊取”)。 例如,這個(gè)函數(shù)將一個(gè)列表(實(shí)例上是任何可變序列)中的所有項(xiàng)設(shè)置為一個(gè)給定的條目:
intset_all(PyObject *target, PyObject *item){Py_ssize_t i, n;n = PyObject_Length(target);if (n < 0)return -1;for (i = 0; i < n; i++) {PyObject *index = PyLong_FromSsize_t(i);if (!index)return -1;if (PyObject_SetItem(target, index, item) < 0) {Py_DECREF(index);return -1;}Py_DECREF(index);}return 0;}
對(duì)于函數(shù)返回值的情況略有不同。 雖然向大多數(shù)函數(shù)傳遞一個(gè)引用不會(huì)改變你對(duì)該引用的所有權(quán)責(zé)任,但許多返回一個(gè)引用的函數(shù)會(huì)給你該引用的所有權(quán)。 原因很簡(jiǎn)單:在許多情況下,返回的對(duì)象是臨時(shí)創(chuàng)建的,而你得到的引用是對(duì)該對(duì)象的唯一引用。 因此,返回對(duì)象引用的通用函數(shù),如 PyObject_GetItem() 和 PySequence_GetItem(),將總是返回一個(gè)新的引用(調(diào)用方將成為該引用的所有者)。
一個(gè)需要了解的重點(diǎn)在于你是否擁有一個(gè)由函數(shù)返回的引用只取決于你所調(diào)用的函數(shù) —- 附帶物 (作為參數(shù)傳給函數(shù)的對(duì)象的類型) 不會(huì)帶來額外影響! 因此,如果你使用 PyList_GetItem() 從一個(gè)列表提取條目,你并不會(huì)擁有其引用 —- 但是如果你使用 PySequence_GetItem() (它恰好接受完全相同的參數(shù)) 從同一個(gè)列表獲取同樣的條目,你就會(huì)擁有一個(gè)對(duì)所返回對(duì)象的引用。
下面是說明你要如何編寫一個(gè)函數(shù)來計(jì)算一個(gè)整數(shù)列表中條目的示例;一個(gè)是使用 PyList_GetItem(),而另一個(gè)是使用 PySequence_GetItem()。
longsum_list(PyObject *list){Py_ssize_t i, n;long total = 0, value;PyObject *item;n = PyList_Size(list);if (n < 0)return -1; /* Not a list */for (i = 0; i < n; i++) {item = PyList_GetItem(list, i); /* Can't fail */if (!PyLong_Check(item)) continue; /* Skip non-integers */value = PyLong_AsLong(item);if (value == -1 && PyErr_Occurred())/* Integer too big to fit in a C long, bail out */return -1;total += value;}return total;}
longsum_sequence(PyObject *sequence){Py_ssize_t i, n;long total = 0, value;PyObject *item;n = PySequence_Length(sequence);if (n < 0)return -1; /* Has no length */for (i = 0; i < n; i++) {item = PySequence_GetItem(sequence, i);if (item == NULL)return -1; /* Not a sequence, or other failure */if (PyLong_Check(item)) {value = PyLong_AsLong(item);Py_DECREF(item);if (value == -1 && PyErr_Occurred())/* Integer too big to fit in a C long, bail out */return -1;total += value;}else {Py_DECREF(item); /* Discard reference ownership */}}return total;}
類型
There are few other data types that play a significant role in the Python/C API; most are simple C types such as int, long, double and char*. A few structure types are used to describe static tables used to list the functions exported by a module or the data attributes of a new object type, and another is used to describe the value of a complex number. These will be discussed together with the functions that use them.
type Py_ssize_t
Part of the Stable ABI.
一個(gè)使得 sizeof(Py_ssize_t) == sizeof(size_t) 的有符號(hào)整數(shù)類型。 C99 沒有直接定義這樣的東西(size_t 是一個(gè)無符號(hào)整數(shù)類型)。 請(qǐng)參閱 PEP 353 了解詳情。 PY_SSIZE_T_MAX 是 Py_ssize_t 類型的最大正數(shù)值。
異常
Python程序員只需要處理特定需要處理的錯(cuò)誤異常;未處理的異常會(huì)自動(dòng)傳遞給調(diào)用者,然后傳遞給調(diào)用者的調(diào)用者,依此類推,直到他們到達(dá)頂級(jí)解釋器,在那里將它們報(bào)告給用戶并伴隨堆?;厮荨?/p>
然而,對(duì)于 C 程序員來說,錯(cuò)誤檢查必須總是顯式進(jìn)行的。 Python/C API 中的所有函數(shù)都可以引發(fā)異常,除非在函數(shù)的文檔中另外顯式聲明。 一般來說,當(dāng)一個(gè)函數(shù)遇到錯(cuò)誤時(shí),它會(huì)設(shè)置一個(gè)異常,丟棄它所擁有的任何對(duì)象引用,并返回一個(gè)錯(cuò)誤標(biāo)示。 如果沒有說明例外的文檔,這個(gè)標(biāo)示將為 NULL 或 -1,具體取決于函數(shù)的返回類型。 有少量函數(shù)會(huì)返回一個(gè)布爾真/假結(jié)果值,其中假值表示錯(cuò)誤。 有極少的函數(shù)沒有顯式的錯(cuò)誤標(biāo)示或是具有不明確的返回值,并需要用 PyErr_Occurred() 來進(jìn)行顯式的檢測(cè)。 這些例外總是會(huì)被明確地記入文檔中。
異常狀態(tài)是在各個(gè)線程的存儲(chǔ)中維護(hù)的(這相當(dāng)于在一個(gè)無線程的應(yīng)用中使用全局存儲(chǔ))。 一個(gè)線程可以處在兩種狀態(tài)之一:異常已經(jīng)發(fā)生,或者沒有發(fā)生。 函數(shù) PyErr_Occurred() 可以被用來檢查此狀態(tài):當(dāng)異常發(fā)生時(shí)它將返回一個(gè)借入的異常類型對(duì)象的引用,在其他情況下則返回 NULL。 有多個(gè)函數(shù)可以設(shè)置異常狀態(tài): PyErr_SetString() 是最常見的(盡管不是最通用的)設(shè)置異常狀態(tài)的函數(shù),而 PyErr_Clear() 可以清除異常狀態(tài)。
完整的異常狀態(tài)由三個(gè)對(duì)象組成 (它為都可以為 NULL): 異常類型、相應(yīng)的異常值,以及回溯信息。 這些對(duì)象的含義與 Python 中 sys.exc_info() 的結(jié)果相同;然而,它們并不是一樣的:Python 對(duì)象代表由 Python try … except 語句所處理的最后一個(gè)異常,而 C 層級(jí)的異常狀態(tài)只在異常被傳入到 C 函數(shù)或在它們之間傳遞時(shí)存在直至其到達(dá) Python 字節(jié)碼解釋器的主事件循環(huán),該事件循環(huán)會(huì)負(fù)責(zé)將其轉(zhuǎn)移至 sys.exc_info() 等處。
請(qǐng)注意自 Python 1.5 開始,從 Python 代碼訪問異常狀態(tài)的首選的、線程安全的方式是調(diào)用函數(shù) sys.exc_info(),它將返回 Python 代碼的分線程異常狀態(tài)。 此外,這兩種訪問異常狀態(tài)的方式的語義都發(fā)生了變化因而捕獲到異常的函數(shù)將保存并恢復(fù)其線程的異常狀態(tài)以保留其調(diào)用方的異常狀態(tài)。 這將防止異常處理代碼中由一個(gè)看起來很無辜的函數(shù)覆蓋了正在處理的異常所造成的常見錯(cuò)誤;它還減少了在回溯由棧幀所引用的對(duì)象的往往不被需要的生命其延長(zhǎng)。
作為一般的原則,一個(gè)調(diào)用另一個(gè)函數(shù)來執(zhí)行某些任務(wù)的函數(shù)應(yīng)當(dāng)檢查被調(diào)用的函數(shù)是否引發(fā)了異常,并在引發(fā)異常時(shí)將異常狀態(tài)傳遞給其調(diào)用方。 它應(yīng)當(dāng)丟棄它所擁有的任何對(duì)象引用,并返回一個(gè)錯(cuò)誤標(biāo)示,但它 不應(yīng) 設(shè)置另一個(gè)異常 —- 那會(huì)覆蓋剛引發(fā)的異常,并丟失有關(guān)錯(cuò)誤確切原因的重要信息。
一個(gè)檢測(cè)異常并傳遞它們的簡(jiǎn)單例子在上面的 sum_sequence() 示例中進(jìn)行了演示。 這個(gè)例子恰好在檢測(cè)到錯(cuò)誤時(shí)不需要清理所擁有的任何引用。 下面的示例函數(shù)演示了一些錯(cuò)誤清理操作。 首先,為了向你提示 Python 的優(yōu)勢(shì),我們展示了等效的 Python 代碼:
def incr_item(dict, key):try:item = dict[key]except KeyError:item = 0dict[key] = item + 1
下面是對(duì)應(yīng)的閃耀榮光的 C 代碼:
intincr_item(PyObject *dict, PyObject *key){/* Objects all initialized to NULL for Py_XDECREF */PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;int rv = -1; /* Return value initialized to -1 (failure) */item = PyObject_GetItem(dict, key);if (item == NULL) {/* Handle KeyError only: */if (!PyErr_ExceptionMatches(PyExc_KeyError))goto error;/* Clear the error and use zero: */PyErr_Clear();item = PyLong_FromLong(0L);if (item == NULL)goto error;}const_one = PyLong_FromLong(1L);if (const_one == NULL)goto error;incremented_item = PyNumber_Add(item, const_one);if (incremented_item == NULL)goto error;if (PyObject_SetItem(dict, key, incremented_item) < 0)goto error;rv = 0; /* Success *//* Continue with cleanup code */error:/* Cleanup code, shared by success and failure path *//* Use Py_XDECREF() to ignore NULL references */Py_XDECREF(item);Py_XDECREF(const_one);Py_XDECREF(incremented_item);return rv; /* -1 for error, 0 for success */}
這個(gè)例子代表了 C 語言中 goto 語句一種受到認(rèn)可的用法! 它說明了如何使用 PyErr_ExceptionMatches() 和 PyErr_Clear() 來處理特定的異常,以及如何使用 Py_XDECREF() 來處理可能為 NULL 的自有引用(注意名稱中的 'X';Py_DECREF() 在遇到 NULL 引用時(shí)將會(huì)崩潰)。 重要的一點(diǎn)在于用來保存自有引用的變量要被初始化為 NULL 才能發(fā)揮作用;類似地,建議的返回值也要被初始化為 -1 (失敗) 并且只有在最終執(zhí)行的調(diào)用成功后才會(huì)被設(shè)置為成功。
嵌入Python
只有 Python 解釋器的嵌入方(相對(duì)于擴(kuò)展編寫者而言)才需要擔(dān)心的一項(xiàng)重要任務(wù)是它的初始化,可能還有它的最終化。 解釋器的大多數(shù)功能只有在解釋器被初始化之后才能被使用。
基本的初始化函數(shù)是 Py_Initialize()。 此函數(shù)將初始化已加載模塊表,并創(chuàng)建基本模塊 builtins, __main__ 和 sys。 它還將初始化模塊搜索路徑 (sys.path)。
Py_Initialize() does not set the “script argument list” (sys.argv). If this variable is needed by Python code that will be executed later, setting PyConfig.argv and PyConfig.parse_argv must be set: see Python Initialization Configuration.
在大多數(shù)系統(tǒng)上(特別是 Unix 和 Windows,雖然在細(xì)節(jié)上有所不同),Py_Initialize() 將根據(jù)對(duì)標(biāo)準(zhǔn) Python 解釋器可執(zhí)行文件的位置的最佳猜測(cè)來計(jì)算模塊搜索路徑,并設(shè)定 Python 庫(kù)可在相對(duì)于 Python 解釋器可執(zhí)行文件的固定位置上找到。 特別地,它將相對(duì)于在 shell 命令搜索路徑 (環(huán)境變量 PATH) 上找到的名為 python 的可執(zhí)行文件所在父目錄中查找名為 lib/python*X.Y* 的目錄。
舉例來說,如果 Python 可執(zhí)行文件位于 /usr/local/bin/python,它將假定庫(kù)位于 /usr/local/lib/python*X.Y*。 (實(shí)際上,這個(gè)特定路徑還將成為“回退”位置,會(huì)在當(dāng)無法在 PATH 中找到名為 python 的可執(zhí)行文件時(shí)被使用。) 用戶可以通過設(shè)置環(huán)境變量 PYTHONHOME,或通過設(shè)置 PYTHONPATH 在標(biāo)準(zhǔn)路徑之前插入額外的目錄來覆蓋此行為。
嵌入的應(yīng)用程序可以通過在調(diào)用 Py_Initialize() 之前 調(diào)用 Py_SetProgramName(file) 來改變搜索次序。 請(qǐng)注意 PYTHONHOME 仍然會(huì)覆蓋此設(shè)置并且 PYTHONPATH 仍然會(huì)被插入到標(biāo)準(zhǔn)路徑之前。 需要完全控制權(quán)的應(yīng)用程序必須提供它自己的 Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() 和 Py_GetProgramFullPath() 實(shí)現(xiàn)(這些函數(shù)均在 Modules/getpath.c 中定義)。
有時(shí),還需要對(duì) Python 進(jìn)行“反初始化”。 例如,應(yīng)用程序可能想要重新啟動(dòng) (再次調(diào)用 Py_Initialize()) 或者應(yīng)用程序?qū)?Python 的使用已經(jīng)完成并想要釋放 Python 所分配的內(nèi)存。 這可以通過調(diào)用 Py_FinalizeEx() 來實(shí)現(xiàn)。 如果當(dāng)前 Python 處于已初始化狀態(tài)則 Py_IsInitialized() 函數(shù)將返回真值。 有關(guān)這些函數(shù)的更多信息將在之后的章節(jié)中給出。 請(qǐng)注意 Py_FinalizeEx() 不會(huì) 釋放所有由 Python 解釋器所分配的內(nèi)存,例如由擴(kuò)展模塊所分配的內(nèi)存目前是不會(huì)被釋放的。
調(diào)試構(gòu)建
Python 可以附帶某些宏來編譯以啟用對(duì)解釋器和擴(kuò)展模塊的額外檢查。 這些檢查會(huì)給運(yùn)行時(shí)增加大量額外開銷因此它們默認(rèn)未被啟用。
A full list of the various types of debugging builds is in the file Misc/SpecialBuilds.txt in the Python source distribution. Builds are available that support tracing of reference counts, debugging the memory allocator, or low-level profiling of the main interpreter loop. Only the most frequently used builds will be described in the remainder of this section.
附帶定義 Py_DEBUG 宏來編譯解釋器將產(chǎn)生通常所稱的 Python 調(diào)試編譯版。 Py_DEBUG 在 Unix 編譯中啟用是通過添加 --with-pydebug 到 ./configure 命令來實(shí)現(xiàn)的。 它也可通過提供非 Python 專屬的 _DEBUG 宏來啟用。 當(dāng) Py_DEBUG 在 Unix 編譯中啟用時(shí),編譯器優(yōu)化將被禁用。
除了下文描述的引用計(jì)數(shù)調(diào)試,還會(huì)執(zhí)行額外檢查,請(qǐng)參閱 Python Debug Build。
定義 Py_TRACE_REFS 將啟用引用追蹤 (參見 configure —with-trace-refs 選項(xiàng))。 當(dāng)定義了此宏時(shí),將通過在每個(gè) PyObject 上添加兩個(gè)額外字段來維護(hù)一個(gè)活動(dòng)對(duì)象的循環(huán)雙鏈列表。 總的分配量也會(huì)被追蹤。 在退出時(shí),所有現(xiàn)存的引用將被打印出來。 (在交互模式下這將在解釋器運(yùn)行每條語句之后發(fā)生)。
有關(guān)更多詳細(xì)信息,請(qǐng)參閱Python源代碼中的 Misc/SpecialBuilds.txt 。
網(wǎng)頁(yè)名稱:創(chuàng)新互聯(lián)Python教程:概述
文章源于:http://m.fisionsoft.com.cn/article/dhdhggj.html


咨詢
建站咨詢
