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

成都創(chuàng)新互聯(lián)公司2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目網(wǎng)站制作、成都網(wǎng)站制作網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元館陶做網(wǎng)站,已為上家服務(wù),為館陶各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792
編寫擴展模塊的過程相對來說更易于理解,可以通過“菜譜”的形式分步驟介紹。使用某些工具可在一定程度上自動化這一過程。雖然人們在其他應(yīng)用中嵌入 Python 的做法早已有之,但嵌入 Python 的過程沒有編寫擴展模塊那樣方便直觀。
許多 API 函數(shù)在你嵌入或是擴展 Python 這兩種場景下都能發(fā)揮作用;此外,大多數(shù)嵌入 Python 的應(yīng)用程序也需要提供自定義擴展,因此在嘗試在實際應(yīng)用中嵌入 Python 之前先熟悉編寫擴展應(yīng)該會是個好主意。
代碼標準
如果你想要編寫可包含于 CPython 的 C 代碼,你 必須 遵循在 PEP 7 中定義的指導(dǎo)原則和標準。這些指導(dǎo)原則適用于任何你所要擴展的 Python 版本。在編寫你自己的第三方擴展模塊時可以不必遵循這些規(guī)范,除非你準備在日后向 Python 貢獻這些模塊。
包含文件
使用 Python/C API 所需要的全部函數(shù)、類型和宏定義可通過下面這行語句包含到你的代碼之中:
#define PY_SSIZE_T_CLEAN#include
這意味著包含以下標準頭文件:,,,, 和 (如果可用)。
備注
由于 Python 可能會定義一些能在某些系統(tǒng)上影響標準頭文件的預(yù)處理器定義,因此在包含任何標準頭文件之前,你 必須 先包含 Python.h。
推薦總是在 Python.h 前定義 PY_SSIZE_T_CLEAN 。查看 解析參數(shù)并構(gòu)建值變量 來了解這個宏的更多內(nèi)容。
Python.h 所定義的全部用戶可見名稱(由包含的標準頭文件所定義的除外)都帶有前綴 Py 或者 _Py。以 _Py 打頭的名稱是供 Python 實現(xiàn)內(nèi)部使用的,不應(yīng)被擴展編寫者使用。結(jié)構(gòu)成員名稱沒有保留前綴。
備注
用戶代碼永遠不應(yīng)該定義以 Py 或 _Py 開頭的名稱。這會使讀者感到困惑,并危及用戶代碼對未來Python版本的可移植性,這些版本可能會定義以這些前綴之一開頭的其他名稱。
頭文件通常會與 Python 一起安裝。在 Unix 上,它們位于以下目錄:*prefix*/include/pythonversion/ 和 *exec_prefix*/include/pythonversion/,其中 prefix 和 exec_prefix 是由向 Python 的 configure 腳本傳入的對應(yīng)形參所定義,而 version 則為 '%d.%d' % sys.version_info[:2]。在 Windows 上,頭文件安裝于 *prefix*/include,其中 prefix 是向安裝程序指定的安裝目錄。
要包含頭文件,請將兩個目錄(如果不同)都放到你所用編譯器的包含搜索路徑中。請 不要 將父目錄放入搜索路徑然后使用 #include ;這將使得多平臺編譯不可用,因為 prefix 下平臺無關(guān)的頭文件需要包含來自 exec_prefix 下特定平臺的頭文件。
C++ 用戶應(yīng)該注意,盡管 API 是完全使用 C 來定義的,但頭文件正確地將入口點聲明為 extern "C",因此 API 在 C++ 中使用此 API 不必再做任何特殊處理。
有用的宏
Python 頭文件中定義了一些有用的宏。許多是在靠近它們被使用的地方定義的(例如 Py_RETURN_NONE)。其他更為通用的則定義在這里。這里所顯示的并不是一個完整的列表。
Py_ABS(x)
返回 x 的絕對值。
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ù)類型。這個宏將 c 強制轉(zhuǎn)換為 unsigned char 返回。
Py_DEPRECATED(version)
棄用聲明。該宏必須放置在符號名稱前。
示例:
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 當中的最大值。
3.3 新版功能.
Py_MEMBER_SIZE(type, member)
返回結(jié)構(gòu) (type) member 的大小,以字節(jié)表示。
3.6 新版功能.
Py_MIN(x, y)
返回 x 和 y 當中的最小值。
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()
這個可以在你有一個設(shè)計上無法到達的代碼路徑時使用。例如,當一個 switch 語句中所有可能的值都已被 case 子句覆蓋了,就可將其用在 default: 子句中。當你非常想在某個位置放一個 assert(0) 或 abort() 調(diào)用時也可以用這個。
在 release 模式下,該宏幫助編譯器優(yōu)化代碼,并避免發(fā)出不可到達代碼的警告。例如,在 GCC 的 release 模式下,該宏使用 __builtin_unreachable() 實現(xiàn)。
Py_UNREACHABLE() 的一個用法是調(diào)用一個不會返回,但卻沒有聲明 _Py_NO_RETURN 的函數(shù)之后。
如果一個代碼路徑不太可能是正常代碼,但在特殊情況下可以到達,就不能使用該宏。例如,在低內(nèi)存條件下,或者一個系統(tǒng)調(diào)用返回超出預(yù)期范圍值,諸如此類,最好將錯誤報告給調(dià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)建一個可以在文檔字符串中使用的,名字為 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)建一個文檔字符串,或者當文檔字符串被禁用時,創(chuàng)建一個空字符串。
如 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}};
對象、類型和引用計數(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 對象(甚至 Python 整數(shù))都有一個 type 和一個 reference count。對象的類型確定它是什么類型的對象(例如整數(shù)、列表或用戶定義函數(shù);還有更多,如 標準類型層級結(jié)構(gòu) 中所述)。對于每個眾所周知的類型,都有一個宏來檢查對象是否屬于該類型;例如,當(且僅當) a 所指的對象是 Python 列表時 PyList_Check(a) 為真。
引用計數(shù)
引用計數(shù)非常重要,因為現(xiàn)代計算機內(nèi)存(通常十分)有限;它計算有多少不同的地方引用同一個對象。這樣的地方可以是某個對象,或者是某個全局(或靜態(tài))C 變量,亦或是某個 C 函數(shù)的局部變量。當一個對象的引用計數(shù)變?yōu)?0,釋放該對象。如果這個已釋放的對象包含其它對象的引用計數(shù),則遞減這些對象的引用計數(shù)。如果這些對象的引用計數(shù)減少為零,則可以依次釋放這些對象,依此類推。(這里有一個很明顯的問題——對象之間相互引用;目前,解決方案是“不要那樣做”。)
總是顯式操作引用計數(shù)。通常的方法是使用宏 Py_INCREF() 來增加一個對象的引用計數(shù),使用宏 Py_DECREF() 來減少一個對象的引用計數(shù)。宏 Py_DECREF() 必須檢查引用計數(shù)是否為零,然后調(diào)用對象的釋放器, 因此它比 incref 宏復(fù)雜得多。釋放器是一個包含在對象類型結(jié)構(gòu)中的函數(shù)指針。如果對象是復(fù)合對象類型(例如列表),則類型特定的釋放器負責遞減包含在對象中的其他對象的引用計數(shù),并執(zhí)行所需的終結(jié)。引用計數(shù)不會溢出,至少用與虛擬內(nèi)存中不同內(nèi)存位置一樣多的位用于保存引用計數(shù)(即 sizeof(Py_ssize_t) >= sizeof(void*) )。因此,引用計數(shù)遞增是一個簡單的操作。
沒有必要為每個包含指向?qū)ο蟮闹羔樀木植孔兞吭黾訉ο蟮囊糜嫈?shù)。理論上,當變量指向?qū)ο髸r,對象的引用計數(shù)增加 1 ,當變量超出范圍時,對象的引用計數(shù)減少 1 。但是,這兩者相互抵消,所以最后引用計數(shù)沒有改變。使用引用計數(shù)的唯一真正原因是只要我們的變量指向它,就可以防止對象被釋放。如果知道至少有一個對該對象的其他引用存活時間至少和我們的變量一樣長,則沒必要臨時增加引用計數(shù)。一個典型的情形是,對象作為參數(shù)從 Python 中傳遞給被調(diào)用的擴展模塊中的 C 函數(shù)時,調(diào)用機制會保證在調(diào)用期間持有對所有參數(shù)的引用。
但是,有一個常見的陷阱是從列表中提取一個對象,并將其持有一段時間,而不增加其引用計數(shù)。某些操作可能會從列表中刪除某個對象,減少其引用計數(shù),并有可能重新分配這個對象。真正的危險是,這個看似無害的操作可能會調(diào)用任意 Python 代碼——也許有一個代碼路徑允許控制流從 Py_DECREF() 回到用戶,因此在復(fù)合對象上的操作都存在潛在的風險。
一個安全的方式是始終使用泛型操作(名稱以 PyObject_ , PyNumber_ , PySequence_ 或 PyMapping_ 開頭的函數(shù))。這些操作總是增加它們返回的對象的引用計數(shù)。這讓調(diào)用者有責任在獲得結(jié)果后調(diào)用 Py_DECREF() 。習慣這種方式很簡單。
引用計數(shù)細節(jié)
Python/C API 中函數(shù)引用計數(shù)行為最好是通過 引用所有權(quán) 來解釋。 所有權(quán)是關(guān)聯(lián)到引用,而不是對象(對象沒有所有權(quán):它們總是會被共享)。 “獲得引用所有權(quán)”意味著當不再需要該引用時必須在其上調(diào)用 Py_DECREF。 所有權(quán)也可以被轉(zhuǎn)移,這意味著接受該引用所有權(quán)的代碼當不再需要該引用時必須通過調(diào)用 Py_DECREF() 或 Py_XDECREF() 來最終撤銷引用 —- 或是將這個責任轉(zhuǎn)移出去(通常是轉(zhuǎn)給其調(diào)用方)。 當一個函數(shù)將引用所有權(quán)轉(zhuǎn)給其調(diào)用方時,則稱調(diào)用方收到了一個 新的 引用。 當所有權(quán)未被轉(zhuǎn)移時,則稱調(diào)用方 借入 該引用。 對于 borrowed reference 來說不需任何額外操作。
相反地,當調(diào)用方函數(shù)傳入一個對象的引用時,存在兩種可能:該函數(shù) 竊取 了一個對象的引用,或是沒有竊取。 竊取引用 意味著當你向一個函數(shù)傳入引用時,該函數(shù)會假定它擁有該引用,而你將不再對它負有責任。
很少有函數(shù)會竊取引用;兩個重要的例外是 PyList_SetItem() 和 PyTuple_SetItem(),它們會竊取對條目的引用(但不是條目所在的元組或列表?。?。 這些函數(shù)被設(shè)計為會竊取引用是因為在使用新創(chuàng)建的對象來填充元組或列表時有一個通常的慣例;例如,創(chuàng)建元組 (1, 2, "three") 的代碼看起來可以是這樣的(暫時不要管錯誤處理;下面會顯示更好的代碼編寫方式):
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() 返回了一個新的引用并且它立即被 PyTuple_SetItem() 所竊取。 當你想要繼續(xù)使用一個對象而對它的引用將被竊取時,請在調(diào)用竊取引用的函數(shù)之前使用 Py_INCREF() 來抓取另一個引用。
順便提一下,PyTuple_SetItem() 是設(shè)置元組條目的 唯一 方式;PySequence_SetItem() 和 PyObject_SetItem() 會拒絕這樣做因為元組是不可變數(shù)據(jù)類型。 你應(yīng)當只對你自己創(chuàng)建的元組使用 PyTuple_SetItem()。
等價于填充一個列表的代碼可以使用 PyList_New() 和 PyList_SetItem() 來編寫。
然而,在實踐中,你很少會使用這些創(chuàng)建和填充元組或列表的方式。 有一個通用的函數(shù) Py_BuildValue() 可以根據(jù) C 值來創(chuàng)建大多數(shù)常用對象,由一個 格式字符串 來指明。 例如,上面的兩個代碼塊可以用下面的代碼來代替(還會負責錯誤檢測):
PyObject *tuple, *list;tuple = Py_BuildValue("(iis)", 1, 2, "three");list = Py_BuildValue("[iis]", 1, 2, "three");
使用 PyObject_SetItem() 等來處理那些你只是借入引用的條目是更為常見的,例如傳給你正在編寫的函數(shù)的參數(shù)。 在這種情況下,他們對于引用計數(shù)的行為會更為理智,因為你不需要遞增引用計數(shù)以便你可以將引用計數(shù)轉(zhuǎn)出去(“讓它被竊取”)。 例如,這個函數(shù)將一個列表(實例上是任何可變序列)中的所有項設(shè)置為一個給定的條目:
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;}
對于函數(shù)返回值的情況略有不同。 雖然向大多數(shù)函數(shù)傳遞一個引用不會改變你對該引用的所有權(quán)責任,但許多返回一個引用的函數(shù)會給你該引用的所有權(quán)。 原因很簡單:在許多情況下,返回的對象是臨時創(chuàng)建的,而你得到的引用是對該對象的唯一引用。 因此,返回對象引用的通用函數(shù),如 PyObject_GetItem() 和 PySequence_GetItem(),將總是返回一個新的引用(調(diào)用方將成為該引用的所有者)。
一個需要了解的重點在于你是否擁有一個由函數(shù)返回的引用只取決于你所調(diào)用的函數(shù) —- 附帶物 (作為參數(shù)傳給函數(shù)的對象的類型) 不會帶來額外影響! 因此,如果你使用 PyList_GetItem() 從一個列表提取條目,你并不會擁有其引用 —- 但是如果你使用 PySequence_GetItem() (它恰好接受完全相同的參數(shù)) 從同一個列表獲取同樣的條目,你就會擁有一個對所返回對象的引用。
下面是說明你要如何編寫一個函數(shù)來計算一個整數(shù)列表中條目的示例;一個是使用 PyList_GetItem(),而另一個是使用 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.
一個使得 sizeof(Py_ssize_t) == sizeof(size_t) 的有符號整數(shù)類型。 C99 沒有直接定義這樣的東西(size_t 是一個無符號整數(shù)類型)。 請參閱 PEP 353 了解詳情。 PY_SSIZE_T_MAX 是 Py_ssize_t 類型的最大正數(shù)值。
異常
Python程序員只需要處理特定需要處理的錯誤異常;未處理的異常會自動傳遞給調(diào)用者,然后傳遞給調(diào)用者的調(diào)用者,依此類推,直到他們到達頂級解釋器,在那里將它們報告給用戶并伴隨堆?;厮荨?/p>
然而,對于 C 程序員來說,錯誤檢查必須總是顯式進行的。 Python/C API 中的所有函數(shù)都可以引發(fā)異常,除非在函數(shù)的文檔中另外顯式聲明。 一般來說,當一個函數(shù)遇到錯誤時,它會設(shè)置一個異常,丟棄它所擁有的任何對象引用,并返回一個錯誤標示。 如果沒有說明例外的文檔,這個標示將為 NULL 或 -1,具體取決于函數(shù)的返回類型。 有少量函數(shù)會返回一個布爾真/假結(jié)果值,其中假值表示錯誤。 有極少的函數(shù)沒有顯式的錯誤標示或是具有不明確的返回值,并需要用 PyErr_Occurred() 來進行顯式的檢測。 這些例外總是會被明確地記入文檔中。
異常狀態(tài)是在各個線程的存儲中維護的(這相當于在一個無線程的應(yīng)用中使用全局存儲)。 一個線程可以處在兩種狀態(tài)之一:異常已經(jīng)發(fā)生,或者沒有發(fā)生。 函數(shù) PyErr_Occurred() 可以被用來檢查此狀態(tài):當異常發(fā)生時它將返回一個借入的異常類型對象的引用,在其他情況下則返回 NULL。 有多個函數(shù)可以設(shè)置異常狀態(tài): PyErr_SetString() 是最常見的(盡管不是最通用的)設(shè)置異常狀態(tài)的函數(shù),而 PyErr_Clear() 可以清除異常狀態(tài)。
完整的異常狀態(tài)由三個對象組成 (它為都可以為 NULL): 異常類型、相應(yīng)的異常值,以及回溯信息。 這些對象的含義與 Python 中 sys.exc_info() 的結(jié)果相同;然而,它們并不是一樣的:Python 對象代表由 Python try … except 語句所處理的最后一個異常,而 C 層級的異常狀態(tài)只在異常被傳入到 C 函數(shù)或在它們之間傳遞時存在直至其到達 Python 字節(jié)碼解釋器的主事件循環(huán),該事件循環(huán)會負責將其轉(zhuǎn)移至 sys.exc_info() 等處。
請注意自 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)。 這將防止異常處理代碼中由一個看起來很無辜的函數(shù)覆蓋了正在處理的異常所造成的常見錯誤;它還減少了在回溯由棧幀所引用的對象的往往不被需要的生命其延長。
作為一般的原則,一個調(diào)用另一個函數(shù)來執(zhí)行某些任務(wù)的函數(shù)應(yīng)當檢查被調(diào)用的函數(shù)是否引發(fā)了異常,并在引發(fā)異常時將異常狀態(tài)傳遞給其調(diào)用方。 它應(yīng)當丟棄它所擁有的任何對象引用,并返回一個錯誤標示,但它 不應(yīng) 設(shè)置另一個異常 —- 那會覆蓋剛引發(fā)的異常,并丟失有關(guān)錯誤確切原因的重要信息。
一個檢測異常并傳遞它們的簡單例子在上面的 sum_sequence() 示例中進行了演示。 這個例子恰好在檢測到錯誤時不需要清理所擁有的任何引用。 下面的示例函數(shù)演示了一些錯誤清理操作。 首先,為了向你提示 Python 的優(yōu)勢,我們展示了等效的 Python 代碼:
def incr_item(dict, key):try:item = dict[key]except KeyError:item = 0dict[key] = item + 1
下面是對應(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 */}
這個例子代表了 C 語言中 goto 語句一種受到認可的用法! 它說明了如何使用 PyErr_ExceptionMatches() 和 PyErr_Clear() 來處理特定的異常,以及如何使用 Py_XDECREF() 來處理可能為 NULL 的自有引用(注意名稱中的 'X';Py_DECREF() 在遇到 NULL 引用時將會崩潰)。 重要的一點在于用來保存自有引用的變量要被初始化為 NULL 才能發(fā)揮作用;類似地,建議的返回值也要被初始化為 -1 (失敗) 并且只有在最終執(zhí)行的調(diào)用成功后才會被設(shè)置為成功。
嵌入Python
只有 Python 解釋器的嵌入方(相對于擴展編寫者而言)才需要擔心的一項重要任務(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,雖然在細節(jié)上有所不同),Py_Initialize() 將根據(jù)對標準 Python 解釋器可執(zhí)行文件的位置的最佳猜測來計算模塊搜索路徑,并設(shè)定 Python 庫可在相對于 Python 解釋器可執(zhí)行文件的固定位置上找到。 特別地,它將相對于在 shell 命令搜索路徑 (環(huán)境變量 PATH) 上找到的名為 python 的可執(zhí)行文件所在父目錄中查找名為 lib/python*X.Y* 的目錄。
舉例來說,如果 Python 可執(zhí)行文件位于 /usr/local/bin/python,它將假定庫位于 /usr/local/lib/python*X.Y*。 (實際上,這個特定路徑還將成為“回退”位置,會在當無法在 PATH 中找到名為 python 的可執(zhí)行文件時被使用。) 用戶可以通過設(shè)置環(huán)境變量 PYTHONHOME,或通過設(shè)置 PYTHONPATH 在標準路徑之前插入額外的目錄來覆蓋此行為。
嵌入的應(yīng)用程序可以通過在調(diào)用 Py_Initialize() 之前 調(diào)用 Py_SetProgramName(file) 來改變搜索次序。 請注意 PYTHONHOME 仍然會覆蓋此設(shè)置并且 PYTHONPATH 仍然會被插入到標準路徑之前。 需要完全控制權(quán)的應(yīng)用程序必須提供它自己的 Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() 和 Py_GetProgramFullPath() 實現(xiàn)(這些函數(shù)均在 Modules/getpath.c 中定義)。
有時,還需要對 Python 進行“反初始化”。 例如,應(yīng)用程序可能想要重新啟動 (再次調(diào)用 Py_Initialize()) 或者應(yīng)用程序?qū)?Python 的使用已經(jīng)完成并想要釋放 Python 所分配的內(nèi)存。 這可以通過調(diào)用 Py_FinalizeEx() 來實現(xiàn)。 如果當前 Python 處于已初始化狀態(tài)則 Py_IsInitialized() 函數(shù)將返回真值。 有關(guān)這些函數(shù)的更多信息將在之后的章節(jié)中給出。 請注意 Py_FinalizeEx() 不會 釋放所有由 Python 解釋器所分配的內(nèi)存,例如由擴展模塊所分配的內(nèi)存目前是不會被釋放的。
調(diào)試構(gòu)建
Python 可以附帶某些宏來編譯以啟用對解釋器和擴展模塊的額外檢查。 這些檢查會給運行時增加大量額外開銷因此它們默認未被啟用。
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 命令來實現(xiàn)的。 它也可通過提供非 Python 專屬的 _DEBUG 宏來啟用。 當 Py_DEBUG 在 Unix 編譯中啟用時,編譯器優(yōu)化將被禁用。
除了下文描述的引用計數(shù)調(diào)試,還會執(zhí)行額外檢查,請參閱 Python Debug Build。
定義 Py_TRACE_REFS 將啟用引用追蹤 (參見 configure —with-trace-refs 選項)。 當定義了此宏時,將通過在每個 PyObject 上添加兩個額外字段來維護一個活動對象的循環(huán)雙鏈列表。 總的分配量也會被追蹤。 在退出時,所有現(xiàn)存的引用將被打印出來。 (在交互模式下這將在解釋器運行每條語句之后發(fā)生)。
有關(guān)更多詳細信息,請參閱Python源代碼中的 Misc/SpecialBuilds.txt 。
網(wǎng)頁題目:創(chuàng)新互聯(lián)Python教程:概述
網(wǎng)址分享:http://www.5511xx.com/article/dhdhggj.html


咨詢
建站咨詢
