### 導航
- [索引](../genindex.xhtml "總目錄")
- [模塊](../py-modindex.xhtml "Python 模塊索引") |
- [下一頁](newtypes_tutorial.xhtml "2. 自定義擴展類型:教程") |
- [上一頁](index.xhtml "擴展和嵌入 Python 解釋器") |
- 
- [Python](https://www.python.org/) ?
- zh\_CN 3.7.3 [文檔](../index.xhtml) ?
- [擴展和嵌入 Python 解釋器](index.xhtml) ?
- $('.inline-search').show(0); |
# 1. 使用 C 或 C++ 擴展 Python
如果你會用 C,添加新的 Python 內置模塊會很簡單。以下兩件不能用 Python 直接做的事,可以通過 *extension modules* 來實現:實現新的內置對象類型;調用 C 的庫函數和系統調用。
為了支持擴展,Python API(應用程序編程接口)定義了一系列函數、宏和變量,可以訪問 Python 運行時系統的大部分內容。Python 的 API 可以通過在一個 C 源文件中引用 `"Python.h"` 頭文件來使用。
擴展模塊的編寫方式取決與你的目的以及系統設置;下面章節會詳細介紹。
注解
C擴展接口特指CPython,擴展模塊無法在其他Python實現上工作。在大多數情況下,應該避免寫C擴展,來保持可移植性。舉個例子,如果你的用例調用了C庫或系統調用,你應該考慮使用 [`ctypes`](../library/ctypes.xhtml#module-ctypes "ctypes: A foreign function library for Python.") 模塊或 [cffi](https://cffi.readthedocs.io/) \[https://cffi.readthedocs.io/\] 庫,而不是自己寫C代碼。這些模塊允許你寫Python代碼來接口C代碼,而且可移植性更好。不知為何編譯失敗了。
## 1.1. 一個簡單的例子
讓我們創建一個擴展模塊 `spam` (Monty Python 粉絲最喜歡的食物...) 并且想要創建對應 C 庫函數 `system()` [1](#id5) 的 Python 接口。 這個函數接受一個以 null 結尾的字符串參數并返回一個整數。 我們希望可以在 Python 中以如下方式調用此函數:
```
>>> import spam
>>> status = spam.system("ls -l")
```
首先創建一個 `spammodule.c` 文件。(傳統上,如果一個模塊叫 `spam`,則對應實現它的 C 文件叫 `spammodule.c`;如果這個模塊名字非常長,比如 `spammify`,則這個模塊的文件可以直接叫 `spammify.c`。)
文件中開始的兩行是:
```
#define PY_SSIZE_T_CLEAN
#include <Python.h>
```
這會導入 Python API(如果你喜歡,你可以在這里添加描述模塊目標和版權信息的注釋)。
注解
由于Python可能會定義一些影響某些系統上標準頭文件的預處理器定義,因此在包含任何標準頭文件之前,您\*必須\* include 這個文件:Python.h。
推薦總是在 `Python.h` 前定義 `PY_SSIZE_T_CLEAN` 。查看 [提取擴展函數的參數](#parsetuple) 來了解這個宏的更多內容。
所有用戶可見的符號都定義自 `Python.h` 中,并擁有前綴 `Py` 或 `PY` ,除了那些已經定義在標準頭文件的。 為了方便,以及由于其在 Python 解釋器中廣泛應用,`"Python.h"` 也包含了少量標準頭文件: `<stdio.h>`,`<string.h>`,`<errno.h>` 和 `<stdlib.h>`。 如果后面的頭文件在你的系統上不存在,還會直接聲明函數 `malloc()`,`free()` 和 `realloc()` 。
下面要做的事是將 C 函數添加到我們的擴展模塊,當 Python 表達式 `spam.system(string)` 被求值時函數將被調用(我們很快就會看到它最終是如何被調用的):
```
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
```
有個直接翻譯參數列表的方法(例如單獨的 `“ls-l"`)到要傳遞給C函數的參數。C函數總是有兩個參數,通常名字是 *self* 和 *args* 。
對模塊級函數, *self* 參數指向模塊對象;對于對象實例則指向方法。
*args* 參數是指向一個 Python 的 tuple 對象的指針,其中包含參數。 每個 tuple 項對應一個調用參數。 這些參數也全都是 Python 對象 --- 要在我們的 C 函數中使用它們就需要先將其轉換為 C 值。 Python API 中的函數 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 會檢查參數類型并將其轉換為 C 值。 它使用模板字符串確定需要的參數類型以及存儲被轉換的值的 C 變量類型。 細節將稍后說明。
[`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 正常返回非零,并已經按照提供的地址存入了各個變量值。如果出錯(零)則應該讓函數返回NULL以通知解釋器出錯(有如例子中看到的)。
## 1.2. 關于錯誤和異常
一個Python解釋器的常見慣例是,函數發生錯誤時,應該設置一個異常環境并返回錯誤值(通常是 *NULL* 指針)。異常存儲在解釋器靜態全局變量中,如果為 *NULL* ,則沒有發生異常。異常的第一個參數也需要保存在靜態全局變量中,也就是raise的第二個參數(第二個參數到 [`raise`](../reference/simple_stmts.xhtml#raise))。第三個變量包含棧回溯信息。這三個變量等同于Python變量 [`sys.exc_info()`](../library/sys.xhtml#sys.exc_info "sys.exc_info") (查看Python庫參考的模塊 [`sys`](../library/sys.xhtml#module-sys "sys: Access system-specific parameters and functions.") 的章節)。這對于理解到底發生了什么錯誤是很重要的。
Python API中定義了一些函數來設置這些變量。
最常用的就是 [`PyErr_SetString()`](../c-api/exceptions.xhtml#c.PyErr_SetString "PyErr_SetString")。 其參數是異常對象和 C 字符串。 異常對象一般是像 `PyExc_ZeroDivisionError` 這樣的預定義對象。 C 字符串指明異常原因,并被轉換為一個 Python 字符串對象存儲為異常的“關聯值”。
另一個有用的函數是 [`PyErr_SetFromErrno()`](../c-api/exceptions.xhtml#c.PyErr_SetFromErrno "PyErr_SetFromErrno") ,僅接受一個異常對象,異常描述包含在全局變量 `errno` 中。最通用的函數還是 [`PyErr_SetObject()`](../c-api/exceptions.xhtml#c.PyErr_SetObject "PyErr_SetObject") ,包含兩個參數,分別為異常對象和異常描述。你不需要使用 [`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF") 來增加傳遞到其他函數的參數對象的引用計數。
你可以通過 [`PyErr_Occurred()`](../c-api/exceptions.xhtml#c.PyErr_Occurred "PyErr_Occurred") 獲知當前異常,返回當前異常對象,如果確實沒有則為 *NULL* 。一般來說,你在調用函數時不需要調用 [`PyErr_Occurred()`](../c-api/exceptions.xhtml#c.PyErr_Occurred "PyErr_Occurred") 檢查是否發生了異常,你可以直接檢查返回值。
當函數 *f* 調用另一個函數 *g* 時檢測到后者出錯了,*f* 自身將返回一個錯誤值 (通常為 *NULL* 或 `-1`)。 它 *不應* 調用某個 `PyErr_*()` 函數 --- 這種函數已經由 *g* 調用過了。 然后 *f* 的調用者也應該返回一個錯誤提示 *它的* 調用者,同樣 *不應* 調用 `PyErr_*()` ,依此類推 --- 錯誤的最詳細原因已經由首先檢測到它的函數報告了。 一旦這個錯誤到達了 Python 解釋器的主循環,它將中斷當前執行的 Python 代碼并嘗試找到由 Python 程序員所指定的異常處理。
(在某些情況下,當模塊確實能夠通過調用其它 `PyErr_*()` 函數給出更加詳細的錯誤消息,并且在這些情況是可以這樣做的。 但是按照一般規則,這是不必要的,并可能導致有關錯誤原因的信息丟失:大多數操作會由于種種原因而失敗。)
想要忽略由一個失敗的函數調用所設置的異常,異常條件必須通過調用 [`PyErr_Clear()`](../c-api/exceptions.xhtml#c.PyErr_Clear "PyErr_Clear") 顯式地被清除。 C 代碼應當調用 [`PyErr_Clear()`](../c-api/exceptions.xhtml#c.PyErr_Clear "PyErr_Clear") 的唯一情況是如果它不想將錯誤傳給解釋器而是想完全由自己來處理它(可能是嘗試其他方法,或是假裝沒有出錯)。
每次失敗的 `malloc()` 調用必須轉換為一個異常。 `malloc()` (或 `realloc()` )的直接調用者必須調用 [`PyErr_NoMemory()`](../c-api/exceptions.xhtml#c.PyErr_NoMemory "PyErr_NoMemory") 來返回錯誤來提示。所有對象創建函數(例如 [`PyLong_FromLong()`](../c-api/long.xhtml#c.PyLong_FromLong "PyLong_FromLong") )已經這么做了,所以這個提示僅用于直接調用 `malloc()` 的情況。
還要注意的是,除了 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 等重要的例外,返回整數狀態碼的函數通常都是返回正值或零來表示成功,而以 `-1` 表示失敗,如同 Unix 系統調用一樣。
最后,當你返回一個錯誤指示器時要注意清理垃圾(通過為你已經創建的對象執行 [`Py_XDECREF()`](../c-api/refcounting.xhtml#c.Py_XDECREF "Py_XDECREF") 或 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 調用)!
選擇引發哪個異常完全取決于你的喜好。 所有內置的 Python 異常都有對應的預聲明 C 對象,例如 `PyExc_ZeroDivisionError`,你可以直接使用它們。 當然,你應當明智地選擇異常 --- 不要使用 `PyExc_TypeError` 來表示一個文件無法被打開 (那大概應該用 `PyExc_IOError`)。 如果參數列表有問題,[`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 函數通常會引發 `PyExc_TypeError`。 如果你想要一個參數的值必須處于特定范圍之內或必須滿足其他條件,則適宜使用 `PyExc_ValueError`。
你也可以為你的模塊定義一個唯一的新異常。需要在文件前部聲明一個靜態對象變量,如:
```
static PyObject *SpamError;
```
以及初始化你的模塊的初始化函數 (`PyInit_spam()`) 包含一個異常對象(先不管錯誤檢查):
```
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m, "error", SpamError);
return m;
}
```
注意實際的Python異常名字是 `spam.error` 。 [`PyErr_NewException()`](../c-api/exceptions.xhtml#c.PyErr_NewException "PyErr_NewException") 函數使用 [`Exception`](../library/exceptions.xhtml#Exception "Exception") 為基類創建一個類(除非是使用另外一個類替代 *NULL* )。描述參考 [內置異常](../library/exceptions.xhtml#bltin-exceptions) 。
同樣注意的是創建類保存了 `SpamError` 的一個引用,這是有意的。為了防止被垃圾回收掉,否則 `SpamError` 隨時會成為野指針。
一會討論 `PyMODINIT_FUNC` 作為函數返回類型的用法。
`spam.error` 異常可以在擴展模塊中拋出,通過 [`PyErr_SetString()`](../c-api/exceptions.xhtml#c.PyErr_SetString "PyErr_SetString") 函數調用,如下:
```
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
```
## 1.3. 回到例子
回到前面的例子,你應該明白下面的代碼:
```
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
```
如果在參數列表中檢測到錯誤,將會返回 *NULL* (返回對象指針的函數的錯誤指示器) , 依據 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 所設置的異常。 在其他情況下參數的字符串值會被拷貝到局部變量 `command`。 這是一個指針賦值,你不應該修改它所指向的字符串 (所以在標準 C 中,變量 `command` 應當被正確地聲明為 `const char *command`)。
下一個語句使用UNIX系統函數 `system()` ,傳遞給他的參數是剛才從 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 取出的:
```
sts = system(command);
```
我們的 `spam.system()` 函數必須返回 `sts` 的值作為Python對象。這通過使用函數 [`PyLong_FromLong()`](../c-api/long.xhtml#c.PyLong_FromLong "PyLong_FromLong") 來實現。
```
return PyLong_FromLong(sts);
```
在這種情況下,會返回一個整數對象,(這個對象會在Python堆里面管理)。
如果你的C函數沒有有用的返回值(返回 `void` 的函數),則必須返回 `None` 。(你可以用 `Py_RETUN_NONE` 宏來完成):
```
Py_INCREF(Py_None);
return Py_None;
```
[`Py_None`](../c-api/none.xhtml#c.Py_None "Py_None") 是一個C名字指定Python對象 `None` 。這是一個真正的PY對象,而不是 *NULL* 指針。
## 1.4. 模塊方法表和初始化函數
為了展示 `spam_system()` 如何被Python程序調用。把函數聲明為可以被Python調用,需要先定義一個方法表 "method table" 。
```
static PyMethodDef SpamMethods[] = {
...
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
...
{NULL, NULL, 0, NULL} /* Sentinel */
};
```
注意第三個參數 ( `METH_VARARGS` ) ,這個標志指定會使用C的調用慣例。可選值有 `METH_VARARGS` 、 `METH_VARARGS | METH_KEYWORDS` 。值 `0` 代表使用 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 的陳舊變量。
如果單獨使用 `METH_VARARGS` ,函數會等待Python傳來tuple格式的參數,并最終使用 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 進行解析。
[`METH_KEYWORDS`](../c-api/structures.xhtml#METH_KEYWORDS "METH_KEYWORDS") 值表示接受關鍵字參數。這種情況下C函數需要接受第三個 `PyObject *` 對象,表示字典參數,使用 [`PyArg_ParseTupleAndKeywords()`](../c-api/arg.xhtml#c.PyArg_ParseTupleAndKeywords "PyArg_ParseTupleAndKeywords") 來解析出參數。
這個方法表必須被模塊定義結構所引用。
```
static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
"spam", /* name of module */
spam_doc, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
SpamMethods
};
```
這個結構體必須傳遞給解釋器的模塊初始化函數。初始化函數必須命名為 `PyInit_name()` ,其中 *name* 是模塊的名字,并應該定義為非 `static` ,且在模塊文件里:
```
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModule_Create(&spammodule);
}
```
注意 PyMODINIT\_FUNC 聲明了函數作為 `PyObject *` 返回類型,聲明任何平臺的鏈接生命,以及給C++生命函數的 `extern "C"` 。
當Python程序首次導入模塊 `spam` 時, `PyInit_spam()` 被調用。(查看后續注釋了解嵌入Python)。他會調用 [`PyModule_Create()`](../c-api/module.xhtml#c.PyModule_Create "PyModule_Create") ,會返回模塊對象,并插入內置函數對象到新創建的模塊里,基于表(一個 [`PyMethodDef`](../c-api/structures.xhtml#c.PyMethodDef "PyMethodDef") 結構體的數組類型)到模塊定義。 [`PyModule_Create()`](../c-api/module.xhtml#c.PyModule_Create "PyModule_Create") 返回一個指向剛創建模塊的指針。也可能因為嚴重錯誤而中止,或返回 *NULL* 在模塊無法初始化成功時。初始化函數必須返回模塊對象給調用者,所以之后會被插入 `sys.modules` 。
當嵌入Python時, `PyInit_spam()` 函數不會被自動調用,除非放在 `PyImport_Inittab` 表里。要添加模塊到初始化表,使用 [`PyImport_AppendInittab()`](../c-api/import.xhtml#c.PyImport_AppendInittab "PyImport_AppendInittab") ,可選的跟著一個模塊的導入。
```
int
main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
exit(1);
}
/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("spam", PyInit_spam);
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);
/* Initialize the Python interpreter. Required. */
Py_Initialize();
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyImport_ImportModule("spam");
...
PyMem_RawFree(program);
return 0;
}
```
注解
要從 `sys.modules` 刪除實體或導入已編譯模塊到一個進程里的多個解釋器(或使用 `fork()` 而沒用 `exec()` )會在一些擴展模塊上產生錯誤。擴展模塊作者可以在初始化內部數據結構時給出警告。
更多關于模塊的現實的例子包含在Python源碼包的 `Modules/xxmodule.c` 中。這些文件可以用作你的代碼模板,或者學習。腳本 modulator.py 包含在源碼發行版或Windows安裝中,提供了一個簡單的GUI,用來聲明需要實現的函數和對象,并且可以生成供填入的模板。腳本在 Tools/modulator/ 目錄。查看README以了解用法。
注解
不像我們的 `spam` 例子, `xxmodule` 使用了 *多階段初始化* (Python3.5開始引入), `PyInit_spam` 會返回一個 PyModuleDef 結構體,然后創建的模塊放到導入機制。細節參考 [**PEP 489**](https://www.python.org/dev/peps/pep-0489) \[https://www.python.org/dev/peps/pep-0489\] 的多階段初始化。
## 1.5. 編譯和鏈接
在你能使用你的新寫的擴展之前,你還需要做兩件事情:使用 Python 系統來編譯和鏈接。如果你使用動態加載,這取決于你使用的操作系統的動態加載機制;更多信息請參考編譯擴展模塊的章節( [構建C/C++擴展](building.xhtml#building) 章節),以及在 Windows 上編譯需要的額外信息( [在Windows平臺編譯C和C++擴展](windows.xhtml#building-on-windows) 章節)。
如果你不使用動態加載,或者想要讓模塊永久性的作為Python解釋器的一部分,就必須修改配置設置,并重新構建解釋器。幸運的是在Unix上很簡單,只需要把你的文件 ( `spammodule.c` 為例) 放在解壓縮源碼發行包的 `Modules/` 目錄下,添加一行到 `Modules/Setup.local` 來描述你的文件:
```
spam spammodule.o
```
然后在頂層目錄運行 **make** 來重新構建解釋器。你也可以在 `Modules/` 子目錄使用 **make**,但是你必須先重建 `Makefile` 文件,然后運行 '**make** Makefile' 命令。(你每次修改 `Setup` 文件都需要這樣操作。)
如果你的模塊需要額外的鏈接,這些內容可以列出在配置文件里,舉個實例:
```
spam spammodule.o -lX11
```
## 1.6. 在C中調用Python函數
迄今為止,我們一直把注意力集中于讓Python調用C函數,其實反過來也很有用,就是用C調用Python函數。這在回調函數中尤其有用。如果一個C接口使用回調,那么就要實現這個回調機制。
幸運的是,Python解釋器是比較方便回調的,并給標準Python函數提供了標準接口。(這里就不再詳述解析Python代碼作為輸入的方式,如果有興趣可以參考 `Python/pythonmain.c` 中的 [`-c`](../using/cmdline.xhtml#cmdoption-c) 命令代碼。)
調用Python函數,首先Python程序要傳遞Python函數對象。應該提供個函數(或其他接口)來實現。當調用這個函數時,用全局變量保存Python函數對象的指針,還要調用 ([`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF")) 來增加引用計數,當然不用全局變量也沒什么關系。例如如下:
```
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
```
這個函數必須使用 [`METH_VARARGS`](../c-api/structures.xhtml#METH_VARARGS "METH_VARARGS") 標志注冊到解釋器,這在 [模塊方法表和初始化函數](#methodtable) 章節會描述。 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 函數及其參數的文檔在 [提取擴展函數的參數](#parsetuple) 。
[`Py_XINCREF()`](../c-api/refcounting.xhtml#c.Py_XINCREF "Py_XINCREF") 和 [`Py_XDECREF()`](../c-api/refcounting.xhtml#c.Py_XDECREF "Py_XDECREF") 這兩個宏可以用來增加或減少對象的引用計數,即使參數是 *NULL* 指針,操作也是安全的(但在這個例子中 *temp* 永遠不會為 *NULL*)。更多內容請參考 [引用計數](#refcounts) 段落。
`PyEval_CallObject()` 返回一個Python對象指針表示返回值。該函數有2個參數,都是指向Python對象的指針:Python函數,和參數列表。參數列表必須是tuple對象,其長度是參數數量。要調用無參數的Python函數,可以傳遞NULL或空元組。要用唯一參數調用,傳遞單一元組。 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") 返回元組,當其格式為字符串或多個編碼時,例如:
```
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
```
[`PyObject_CallObject()`](../c-api/object.xhtml#c.PyObject_CallObject "PyObject_CallObject") 返回Python對象指針,這也是Python函數的返回值。 [`PyObject_CallObject()`](../c-api/object.xhtml#c.PyObject_CallObject "PyObject_CallObject") 是一個對其參數 "引用計數無關" 的函數。例子中新的元組創建用于參數列表,并且在 [`PyObject_CallObject()`](../c-api/object.xhtml#c.PyObject_CallObject "PyObject_CallObject") 之后立即使用了 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 。
`PyEval_CallObject()` 的返回值總是“新”的:要么是一個新建的對象;要么是已有對象,但增加了引用計數。所以除非你想把結果保存在全局變量中,你需要對這個值使用 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF"),即使你對里面的內容(特別!)不感興趣。
在你這么做之前,需要先檢查返回值是否是 *NULL* 。如果是,Python函數會終止并拋出異常。如果C代碼調用了從Python傳入的函數 [`PyObject_CallObject()`](../c-api/object.xhtml#c.PyObject_CallObject "PyObject_CallObject") ,因該立即返回錯誤來告知Python調用者,然后解釋器會打印棧回溯,或者調用Python代碼來處理這個異常。如果無法處理,異常會被 [`PyErr_Clear()`](../c-api/exceptions.xhtml#c.PyErr_Clear "PyErr_Clear") 清除,例如:
```
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
```
依賴于具體的回調函數,你還要提供一個參數列表到 `PyEval_CallObject()` 。在某些情況下參數列表是由Python程序提供的,通過接口再傳到回調函數。這樣就可以不改變形式直接傳遞。另外一些時候你要構造一個新的tuple來傳遞參數。最簡單的方法就是 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") 函數構造tuple。例如,你要傳遞一個事件對象時可以用:
```
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
```
注意 `Py_DECREF(arglist)` 所在處會立即調用,在錯誤檢查之前。當然還要注意一些常規的錯誤,比如 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") 可能會遭遇內存不足等等。
你還需要注意,用關鍵字參數調用 [`PyObject_Call()`](../c-api/object.xhtml#c.PyObject_Call "PyObject_Call") ,需要支持普通參數和關鍵字參數。有如如上例子中,我們使用 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") 來構造字典。
```
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
```
## 1.7. 提取擴展函數的參數
函數 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 的聲明如下:
```
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
```
參數 *arg* 必須是一個元組對象,包含從 Python 傳遞給 C 函數的參數列表。*format* 參數必須是一個格式字符串,語法請參考 Python C/API 手冊中的 [語句解釋及變量編譯](../c-api/arg.xhtml#arg-parsing)。剩余參數是各個變量的地址,類型要與格式字符串對應。
注意 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 會檢測他需要的Python參數類型,卻無法檢測傳遞給他的C變量地址,如果這里出錯了,可能會在內存中隨機寫入東西,小心。
注意任何由調用者提供的Python對象引用是 *借來的* 引用;不要遞減它們的引用計數!
一些調用的例子:
```
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>
```
```
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* No arguments */
/* Python call: f() */
```
```
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
/* Possible Python call: f('whoops!') */
```
```
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
/* Possible Python call: f(1, 2, 'three') */
```
```
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* A pair of ints and a string, whose size is also returned */
/* Possible Python call: f((1, 2), 'three') */
```
```
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* A string, and optionally another string and an integer */
/* Possible Python calls:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
```
```
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* A rectangle and a point */
/* Possible Python call:
f(((0, 0), (400, 300)), (10, 10)) */
}
```
```
{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:myfunction", &c);
/* a complex, also providing a function name for errors */
/* Possible Python call: myfunction(1+2j) */
}
```
## 1.8. 給擴展函數的關鍵字參數
函數 [`PyArg_ParseTupleAndKeywords()`](../c-api/arg.xhtml#c.PyArg_ParseTupleAndKeywords "PyArg_ParseTupleAndKeywords") 聲明如下:
```
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
const char *format, char *kwlist[], ...);
```
參數 *arg* 和 *format* 定義同 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 。參數 *kwdict* 是關鍵字字典,用于接受運行時傳來的關鍵字參數。參數 *kwlist* 是一個 *NULL* 結尾的字符串,定義了可以接受的參數名,并從左到右與 *format* 中各個變量對應。如果執行成功 [`PyArg_ParseTupleAndKeywords()`](../c-api/arg.xhtml#c.PyArg_ParseTupleAndKeywords "PyArg_ParseTupleAndKeywords") 會返回true,否則返回false并拋出異常。
注解
嵌套的元組在使用關鍵字參數時無法生效,不在 *kwlist* 中的關鍵字參數會導致 [`TypeError`](../library/exceptions.xhtml#TypeError "TypeError") 異常。
如下是使用關鍵字參數的例子模塊,作者是 Geoff Philbrick ([phibrick@hks.com](mailto:phibrick%40hks.com)):
```
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>
static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdargmodule = {
PyModuleDef_HEAD_INIT,
"keywdarg",
NULL,
-1,
keywdarg_methods
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModule_Create(&keywdargmodule);
}
```
## 1.9. 構造任意值
這個函數與 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 很相似,聲明如下:
```
PyObject *Py_BuildValue(const char *format, ...);
```
接受一個格式字符串,與 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 相同,但是參數必須是原變量的地址指針(輸入給函數,而非輸出)。最終返回一個Python對象適合于返回C函數調用給Python代碼。
一個與 [`PyArg_ParseTuple()`](../c-api/arg.xhtml#c.PyArg_ParseTuple "PyArg_ParseTuple") 的不同是,后面可能需要的要求返回一個元組(Python參數里誒包總是在內部描述為元組),比如用于傳遞給其他Python函數以參數。 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") 并不總是生成元組,在多于1個參數時會生成元組,而如果沒有參數則返回 `None` ,一個參數則直接返回該參數的對象。如果要求強制生成一個長度為空的元組,或包含一個元素的元組,需要在格式字符串中加上括號。
例子(左側是調用,右側是Python值結果):
```
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
```
## 1.10. 引用計數
在C/C++語言中,程序員負責動態分配和回收堆(heap)當中的內存。在C里,通過函數 `malloc()` 和 `free()` 來完成。在C++里是操作 `new` 和 `delete` 來實現相同的功能。
每個由 `malloc()` 分配的內存塊,最終都要由 `free()` 退回到可用內存池里面去。而調用 `free()` 的時機非常重要,如果一個內存塊忘了 `free()` 則會導致內存泄漏,這塊內存在程序結束前將無法重新使用。這叫做 *內存泄漏* 。而如果對同一內存塊 `free()` 了以后,另外一個指針再次訪問,則再次使用 `malloc()` 復用這塊內存會導致沖突。這叫做 *野指針* 。等同于使用未初始化的數據,core dump,錯誤結果,神秘的崩潰等。
內存泄露往往發生在一些并不常見的代碼流程上面。比如一個函數申請了內存以后,做了些計算,然后釋放內存塊。現在一些對函數的修改可能增加對計算的測試并檢測錯誤條件,然后過早的從函數返回了。這很容易忘記在退出前釋放內存,特別是后期修改的代碼。這種內存泄漏,一旦引入,通常很長時間都難以檢測到,錯誤退出被調用的頻度較低,而現代電腦又有非常巨大的虛擬內存,所以泄漏僅在長期運行或頻繁調用泄漏函數時才會變得明顯。因此,有必要避免內存泄漏,通過代碼規范會策略來最小化此類錯誤。
Python通過 `malloc()` 和 `free()` 包含大量的內存分配和釋放,同樣需要避免內存泄漏和野指針。他選擇的方法就是 *引用計數* 。其原理比較簡單:每個對象都包含一個計數器,計數器的增減與對象引用的增減直接相關,當引用計數為0時,表示對象已經沒有存在的意義了,對象就可以刪除了。
另一個叫法是 *自動垃圾回收* 。(有時引用計數也被看作是垃圾回收策略,于是這里的"自動"用以區分兩者)。自動垃圾回收的優點是用戶不需要明確的調用 `free()` 。(另一個優點是改善速度或內存使用,然而這并不難)。缺點是對C,沒有可移植的自動垃圾回收器,而引用計數則可以可移植的實現(只要 `malloc()` 和 `free()` 函數是可用的,這也是C標準擔保的)。也許以后有一天會出現可移植的自動垃圾回收器,但在此前我們必須與引用計數一起工作。
Python使用傳統的引用計數實現,也提供了循環監測器,用以檢測引用循環。這使得應用無需擔心直接或間接的創建了循環引用,這是引用計數垃圾收集的一個弱點。引用循環是對象(可能直接)的引用了本身,所以循環中的每個對象的引用計數都不是0。典型的引用計數實現無法回收處于引用循環中的對象,或者被循環所引用的對象,哪怕沒有循環以外的引用了。
循環探測器可以檢測垃圾循環并回收。 [`gc`](../library/gc.xhtml#module-gc "gc: Interface to the cycle-detecting garbage collector.") 模塊提供了方法運行探測器 ( [`collect()`](../library/gc.xhtml#gc.collect "gc.collect") 函數) ,而且可以在運行時配置禁用探測器。循環探測器被當作可選組件,默認是包含的,也可以在構建時禁用,在Unix平臺(包括Mac OS X)使用 `--without-cycle-gc` 選項到 **configure** 腳本。如果循環探測器被禁用, [`gc`](../library/gc.xhtml#module-gc "gc: Interface to the cycle-detecting garbage collector.") 模塊就不可用了。
### 1.10.1. Python中的引用計數
有兩個宏 `Py_INCREF(x)` 和 `Py_DECREF(x)` ,會處理引用計數的增減。 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 也會在引用計數到達0時釋放對象。為了靈活,并不會直接調用 `free()` ,而是通過對象的 *類型對象* 的函數指針來調用。為了這個目的(或其他的),每個對象同時包含一個指向自身類型對象的指針。
最大的問題依舊:何時使用 `Py_INCREF(x)` 和 `Py_DECREF(x)` ?我們首先引入一些概念。沒有人"擁有"一個對象,你可以 *擁有一個引用* 到一個對象。一個對象的引用計數定義為擁有引用的數量。引用的擁有者有責任調用 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") ,在引用不再需要時。引用的擁有關系可以被傳遞。有三種辦法來處置擁有的引用:傳遞、存儲、調用 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 。忘記處置一個擁有的引用會導致內存泄漏。
還可以 *借用* [2](#id6) 一個對象的引用。借用的引用不應該調用 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 。借用者必須確保不能持有對象超過擁有者借出的時間。在擁有者處置對象后使用借用的引用是有風險的,應該完全避免 [3](#id7) 。
借用相對于引用的優點是你無需擔心整條路徑上代碼的引用,或者說,通過借用你無需擔心內存泄漏的風險。借用的缺點是一些看起來正確代碼上的借用可能會在擁有者處置后使用對象。
借用可以變為擁有引用,通過調用 [`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF") 。這不會影響已經借出的擁有者的狀態。這回創建一個新的擁有引用,并給予完全的擁有者責任(新的擁有者必須恰當的處置引用,就像之前的擁有者那樣)。
### 1.10.2. 擁有規則
當一個對象引用傳遞進出一個函數時,函數的接口應該指定擁有關系的傳遞是否包含引用。
大多數函數返回一個對象的引用,并傳遞引用擁有關系。通常,所有創建對象的函數,例如 [`PyLong_FromLong()`](../c-api/long.xhtml#c.PyLong_FromLong "PyLong_FromLong") 和 [`Py_BuildValue()`](../c-api/arg.xhtml#c.Py_BuildValue "Py_BuildValue") ,會傳遞擁有關系給接收者。即便是對象不是真正新的,你仍然可以獲得對象的新引用。一個實例是 [`PyLong_FromLong()`](../c-api/long.xhtml#c.PyLong_FromLong "PyLong_FromLong") 維護了一個流行值的緩存,并可以返回已緩存項目的新引用。
很多另一個對象提取對象的函數,也會傳遞引用關系,例如 [`PyObject_GetAttrString()`](../c-api/object.xhtml#c.PyObject_GetAttrString "PyObject_GetAttrString") 。這里的情況不夠清晰,一些不太常用的例程是例外的 [`PyTuple_GetItem()`](../c-api/tuple.xhtml#c.PyTuple_GetItem "PyTuple_GetItem") , [`PyList_GetItem()`](../c-api/list.xhtml#c.PyList_GetItem "PyList_GetItem") , [`PyDict_GetItem()`](../c-api/dict.xhtml#c.PyDict_GetItem "PyDict_GetItem") , [`PyDict_GetItemString()`](../c-api/dict.xhtml#c.PyDict_GetItemString "PyDict_GetItemString") 都是返回從元組、列表、字典里借用的引用。
函數 [`PyImport_AddModule()`](../c-api/import.xhtml#c.PyImport_AddModule "PyImport_AddModule") 也會返回借用的引用,哪怕可能會返回創建的對象:這個可能因為一個擁有的引用對象是存儲在 `sys.modules` 里。
當你傳遞一個對象引用到另一個函數時,通常函數是借用出去的。如果需要存儲,就使用 [`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF") 來變成獨立的擁有者。這個規則有兩個重要的例外: [`PyTuple_SetItem()`](../c-api/tuple.xhtml#c.PyTuple_SetItem "PyTuple_SetItem") 和 [`PyList_SetItem()`](../c-api/list.xhtml#c.PyList_SetItem "PyList_SetItem") 。這些函數接受傳遞來的引用關系,哪怕會失敗!(注意 [`PyDict_SetItem()`](../c-api/dict.xhtml#c.PyDict_SetItem "PyDict_SetItem") 及其同類不會接受引用關系,他們是"正常的")。
當一個C函數被Python調用時,會從調用方傳來的參數借用引用。調用者擁有對象的引用,所以借用的引用生命周期可以保證到函數返回。只要當借用的引用需要存儲或傳遞時,就必須轉換為擁有的引用,通過調用 [`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF") 。
Python調用從C函數返回的對象引用時必須是擁有的引用---擁有關系被從函數傳遞給調用者。
### 1.10.3. 危險的薄冰
有少數情況下,借用的引用看起來無害,但卻可能導致問題。這通常是因為解釋器的隱式調用,并可能導致引用擁有者處置這個引用。
首先需要特別注意的情況是使用 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 到一個無關對象,而這個對象的引用是借用自一個列表的元素。舉個實例:
```
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0); /* BUG! */
}
```
這個函數首先借用一個引用 `list[0]` ,然后替換 `list[1]` 為值 `0` ,最后打印借用的引用。看起來無害是吧,但卻不是。
我們跟著控制流進入 [`PyList_SetItem()`](../c-api/list.xhtml#c.PyList_SetItem "PyList_SetItem") 。列表擁有者引用了其所有成員,所以當成員1被替換時,就必須處置原來的成員1。現在假設原來的成員1是用戶定義類的實例,且假設這個類定義了 [`__del__()`](../reference/datamodel.xhtml#object.__del__ "object.__del__") 方法。如果這個類實例的引用計數是1,那么處置動作就會調用 [`__del__()`](../reference/datamodel.xhtml#object.__del__ "object.__del__") 方法。
既然是Python寫的, [`__del__()`](../reference/datamodel.xhtml#object.__del__ "object.__del__") 方法可以執行任意Python代碼。是否可能在 `bug()` 的 `item` 廢止引用呢,是的。假設列表傳遞到 `bug()` 會被 [`__del__()`](../reference/datamodel.xhtml#object.__del__ "object.__del__") 方法所訪問,就可以執行一個語句來實現 `del list[0]` ,然后假設這是最后一個對對象的引用,就需要釋放內存,從而使得 `item` 無效化。
解決方法是,當你知道了問題的根源,就容易了:臨時增加引用計數。正確版本的函數代碼如下:
```
void
no_bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_INCREF(item);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
Py_DECREF(item);
}
```
這是個真實的故事。一個舊版本的Python包含了這個bug的變種,而一些人花費了大量時間在C調試器上去尋找為什么 [`__del__()`](../reference/datamodel.xhtml#object.__del__ "object.__del__") 方法會失敗。
這個問題的第二種情況是借用的引用涉及線程的變種。通常,Python解釋器里多個線程無法進入對方的路徑,因為有個全局鎖保護著Python整個對象空間。但可以使用宏 [`Py_BEGIN_ALLOW_THREADS`](../c-api/init.xhtml#c.Py_BEGIN_ALLOW_THREADS "Py_BEGIN_ALLOW_THREADS") 來臨時釋放這個鎖,重新獲取鎖用 [`Py_END_ALLOW_THREADS`](../c-api/init.xhtml#c.Py_END_ALLOW_THREADS "Py_END_ALLOW_THREADS") 。這通常圍繞在阻塞I/O調用外,使得其他線程可以在等待I/O期間使用處理器。顯然,如下函數會跟之前那個有一樣的問題:
```
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_BEGIN_ALLOW_THREADS
...some blocking I/O call...
Py_END_ALLOW_THREADS
PyObject_Print(item, stdout, 0); /* BUG! */
}
```
### 1.10.4. NULL指針
通常,函數接受對象引用作為參數,而非期待你傳入 *NULL* 指針,你非這么干會導致dump core (或者之后導致core dumps) 。函數返回對象引用時,返回的 *NULL* 用以指示發生了異常。 *NULL* 參數的理由在從其他函數接收時并未測試,如果每個函數都測試 *NULL* ,就會導致大量的冗余測試,并使得代碼運行更慢。
好的方法是僅在 "源頭" 測試 *NULL* ,當一個指針可能是 *NULL* 時,例如 `malloc()` 或者從一個可能拋出異常的函數。
宏 [`Py_INCREF()`](../c-api/refcounting.xhtml#c.Py_INCREF "Py_INCREF") 和 [`Py_DECREF()`](../c-api/refcounting.xhtml#c.Py_DECREF "Py_DECREF") 不會檢查 *NULL* 指針。但他們的變種 [`Py_XINCREF()`](../c-api/refcounting.xhtml#c.Py_XINCREF "Py_XINCREF") 和 [`Py_XDECREF()`](../c-api/refcounting.xhtml#c.Py_XDECREF "Py_XDECREF") 會檢查。
用以檢查對象類型的宏( `Pytype_Check()` )不會檢查 *NULL* 指針,有很多代碼會多次測試一個對象是否是預期的類型,這可能產生冗余的測試。而 *NULL* 檢查沒有冗余。
C函數調用機制會確保傳遞到C函數的參數列表 (例如 `args` )不會是 *NULL* ,實際上會確保總是元組 [4](#id8) 。
把 *NULL* 指針轉義給Python用戶是個嚴重的錯誤。
## 1.11. 在C++中編寫擴展
還可以在C++中編寫擴展模塊,只是有些限制。如果主程序(Python解釋器)是使用C編譯器來編譯和鏈接的,全局或靜態對象的構造器就不能使用。而如果是C++編譯器來鏈接的就沒有這個問題。函數會被Python解釋器調用(通常就是模塊初始化函數)必須聲明為 `extern "C"` 。而是否在 `extern "C" {...}` 里包含Python頭文件則不是那么重要,因為如果定義了符號 `__cplusplus` 則已經是這么聲明的了(所有現代C++編譯器都會定義這個符號)。
## 1.12. 給擴展模塊提供C API
很多擴展模塊提供了新的函數和類型供Python使用,但有時擴展模塊里的代碼也可以被其他擴展模塊使用。例如,一個擴展模塊可以實現一個類型 "collection" 看起來是沒有順序的。就像是Python列表類型,擁有C API允許擴展模塊來創建和維護列表,這個新的集合類型可以有一堆C函數用于給其他擴展模塊直接使用。
開始看起來很簡單:只需要編寫函數(無需聲明為 `static` ),提供恰當的頭文件,以及C API的文檔。實際上在所有擴展模塊都是靜態鏈接到Python解釋器時也是可以正常工作的。當模塊以共享庫鏈接時,一個模塊中的符號定義對另一個模塊不可見。可見的細節依賴于操作系統,一些系統的Python解釋器使用全局命名空間(例如Windows),有些則在鏈接時需要一個嚴格的已導入符號列表(一個例子是AIX),或者提供可選的不同策略(如Unix系列)。即便是符號是全局可見的,你要調用的模塊也可能尚未加載。
可移植性需要不能對符號可見性做任何假設。這意味著擴展模塊里的所有符號都應該聲明為 `static` ,除了模塊的初始化函數,來避免與其他擴展模塊的命名沖突(在段落 [模塊方法表和初始化函數](#methodtable) 中討論) 。這意味著符號應該 *必須* 通過其他導出方式來供其他擴展模塊訪問。
Python提供了一個特別的機制來傳遞C級別信息(指針),從一個擴展模塊到另一個:Capsules。一個Capsule是一個Python數據類型,會保存指針( `void *` )。Capsule只能通過其C API來創建和訪問,但可以像其他Python對象一樣的傳遞。通常,我們可以指定一個擴展模塊命名空間的名字。其他擴展模塊可以導入這個模塊,獲取這個名字的值,然后從Capsule獲取指針。
Capsule可以用多種方式導出C API給擴展模塊。每個函數可以用自己的Capsule,或者所有C API指針可以存儲在一個數組里,數組地址再發布給Capsule。存儲和獲取指針也可以用多種方式,供客戶端模塊使用。
使用的方法,對Capsule的名字很重要。函數 [`PyCapsule_New()`](../c-api/capsule.xhtml#c.PyCapsule_New "PyCapsule_New") 會接受一個名字參數( `const char *` ),你可以傳入 *NULL* 給名字,但強烈建議指定個名字。恰當的命名Capsule提供了一定程度的運行時類型安全;而卻沒有可行的方法來告知我們一個未命名的Capsule。
通常來說,Capsule用于暴露C API,其名字應該遵循如下規范:
```
modulename.attributename
```
便利函數 [`PyCapsule_Import()`](../c-api/capsule.xhtml#c.PyCapsule_Import "PyCapsule_Import") 可以方便的載入通過Capsule提供的C API,僅在Capsule的名字匹配時。這個行為為C API用戶提供了高度的確定性來載入正確的C API。
如下例子展示了將大部分負擔交由導出模塊作者的方法,適用于常用的庫模塊。其會存儲所有C API指針(例子里只有一個)在 `void` 指針的數組里,并使其值變為Capsule。對應的模塊頭文件提供了宏來管理導入模塊和獲取C API指針;客戶端模塊只需要在訪問C API前調用這個宏即可。
導出的模塊修改自 `spam` 模塊,來自 [一個簡單的例子](#extending-simpleexample) 段落。函數 `spam.system()` 不會直接調用C庫函數 `system()` ,但一個函數 `PySpam_System()` 會負責調用,當然現實中會更復雜些(例如添加 "spam" 到每個命令)。函數 `PySpam_System()` 也會導出給其他擴展模塊。
函數 `PySpam_System()` 是個純C函數,聲明 `static` 就像其他地方那樣:
```
static int
PySpam_System(const char *command)
{
return system(command);
}
```
函數 `spam_system()` 按照如下方式修改:
```
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = PySpam_System(command);
return PyLong_FromLong(sts);
}
```
在模塊開頭,在此行后:
```
#include <Python.h>
```
添加另外兩行:
```
#define SPAM_MODULE
#include "spammodule.h"
```
`#define` 用于告知頭文件需要包含給導出的模塊,而不是客戶端模塊。最終,模塊的初始化函數必須負責初始化C API指針數組:
```
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;
/* Initialize the C API pointer array */
PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;
/* Create a Capsule containing the API pointer array's address */
c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);
if (c_api_object != NULL)
PyModule_AddObject(m, "_C_API", c_api_object);
return m;
}
```
注意 `PySpam_API` 聲明為 `static` ;此外指針數組會在 `PyInit_spam()` 結束后消失!
頭文件 `spammodule.h` 里的一堆工作,看起來如下所示:
```
#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Header file for spammodule */
/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
/* Total number of C API pointers */
#define PySpam_API_pointers 1
#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
#else
/* This section is used in modules that use spammodule's API */
static void **PySpam_API;
#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])
/* Return -1 on error, 0 on success.
* PyCapsule_Import will set an exception if there's an error.
*/
static int
import_spam(void)
{
PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
return (PySpam_API != NULL) ? 0 : -1;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(Py_SPAMMODULE_H) */
```
客戶端模塊必須在其初始化函數里按順序調用函數 `import_spam()` (或其他宏)才能訪問函數 `PySpam_System()` 。
```
PyMODINIT_FUNC
PyInit_client(void)
{
PyObject *m;
m = PyModule_Create(&clientmodule);
if (m == NULL)
return NULL;
if (import_spam() < 0)
return NULL;
/* additional initialization can happen here */
return m;
}
```
這種方法的主要缺點是,文件 `spammodule.h` 過于復雜。當然,對每個要導出的函數,基本結構是相似的,所以只需要學習一次。
最后需要提醒的是Capsule提供了額外的功能,用于存儲在Capsule里的指針的內存分配和釋放。細節參考 Python/C API參考手冊的章節 [膠囊](../c-api/capsule.xhtml#capsules) 和Capsule的實現(在Python源碼發行包的 `Include/pycapsule.h` 和 `Objects/pycapsule.c` )。
腳注
[1](#id1)這個函數的接口已經在標準模塊 [`os`](../library/os.xhtml#module-os "os: Miscellaneous operating system interfaces.") 里了,這里作為一個簡單而直接的例子。
[2](#id2)術語"借用"一個引用是不完全正確的:擁有者仍然有引用的拷貝。
[3](#id3)檢查引用計數至少為1 **沒有用** ,引用計數本身可以在已經釋放的內存里,并有可能被其他對象所用。
[4](#id4)當你使用 "舊式" 風格調用約定時,這些保證不成立,盡管這依舊存在于很多舊代碼中。
### 導航
- [索引](../genindex.xhtml "總目錄")
- [模塊](../py-modindex.xhtml "Python 模塊索引") |
- [下一頁](newtypes_tutorial.xhtml "2. 自定義擴展類型:教程") |
- [上一頁](index.xhtml "擴展和嵌入 Python 解釋器") |
- 
- [Python](https://www.python.org/) ?
- zh\_CN 3.7.3 [文檔](../index.xhtml) ?
- [擴展和嵌入 Python 解釋器](index.xhtml) ?
- $('.inline-search').show(0); |
? [版權所有](../copyright.xhtml) 2001-2019, Python Software Foundation.
Python 軟件基金會是一個非盈利組織。 [請捐助。](https://www.python.org/psf/donations/)
最后更新于 5月 21, 2019. [發現了問題](../bugs.xhtml)?
使用[Sphinx](http://sphinx.pocoo.org/)1.8.4 創建。
- Python文檔內容
- Python 有什么新變化?
- Python 3.7 有什么新變化
- 摘要 - 發布重點
- 新的特性
- 其他語言特性修改
- 新增模塊
- 改進的模塊
- C API 的改變
- 構建的改變
- 性能優化
- 其他 CPython 實現的改變
- 已棄用的 Python 行為
- 已棄用的 Python 模塊、函數和方法
- 已棄用的 C API 函數和類型
- 平臺支持的移除
- API 與特性的移除
- 移除的模塊
- Windows 專屬的改變
- 移植到 Python 3.7
- Python 3.7.1 中的重要變化
- Python 3.7.2 中的重要變化
- Python 3.6 有什么新變化A
- 摘要 - 發布重點
- 新的特性
- 其他語言特性修改
- 新增模塊
- 改進的模塊
- 性能優化
- Build and C API Changes
- 其他改進
- 棄用
- 移除
- 移植到Python 3.6
- Python 3.6.2 中的重要變化
- Python 3.6.4 中的重要變化
- Python 3.6.5 中的重要變化
- Python 3.6.7 中的重要變化
- Python 3.5 有什么新變化
- 摘要 - 發布重點
- 新的特性
- 其他語言特性修改
- 新增模塊
- 改進的模塊
- Other module-level changes
- 性能優化
- Build and C API Changes
- 棄用
- 移除
- Porting to Python 3.5
- Notable changes in Python 3.5.4
- What's New In Python 3.4
- 摘要 - 發布重點
- 新的特性
- 新增模塊
- 改進的模塊
- CPython Implementation Changes
- 棄用
- 移除
- Porting to Python 3.4
- Changed in 3.4.3
- What's New In Python 3.3
- 摘要 - 發布重點
- PEP 405: Virtual Environments
- PEP 420: Implicit Namespace Packages
- PEP 3118: New memoryview implementation and buffer protocol documentation
- PEP 393: Flexible String Representation
- PEP 397: Python Launcher for Windows
- PEP 3151: Reworking the OS and IO exception hierarchy
- PEP 380: Syntax for Delegating to a Subgenerator
- PEP 409: Suppressing exception context
- PEP 414: Explicit Unicode literals
- PEP 3155: Qualified name for classes and functions
- PEP 412: Key-Sharing Dictionary
- PEP 362: Function Signature Object
- PEP 421: Adding sys.implementation
- Using importlib as the Implementation of Import
- 其他語言特性修改
- A Finer-Grained Import Lock
- Builtin functions and types
- 新增模塊
- 改進的模塊
- 性能優化
- Build and C API Changes
- 棄用
- Porting to Python 3.3
- What's New In Python 3.2
- PEP 384: Defining a Stable ABI
- PEP 389: Argparse Command Line Parsing Module
- PEP 391: Dictionary Based Configuration for Logging
- PEP 3148: The concurrent.futures module
- PEP 3147: PYC Repository Directories
- PEP 3149: ABI Version Tagged .so Files
- PEP 3333: Python Web Server Gateway Interface v1.0.1
- 其他語言特性修改
- New, Improved, and Deprecated Modules
- 多線程
- 性能優化
- Unicode
- Codecs
- 文檔
- IDLE
- Code Repository
- Build and C API Changes
- Porting to Python 3.2
- What's New In Python 3.1
- PEP 372: Ordered Dictionaries
- PEP 378: Format Specifier for Thousands Separator
- 其他語言特性修改
- New, Improved, and Deprecated Modules
- 性能優化
- IDLE
- Build and C API Changes
- Porting to Python 3.1
- What's New In Python 3.0
- Common Stumbling Blocks
- Overview Of Syntax Changes
- Changes Already Present In Python 2.6
- Library Changes
- PEP 3101: A New Approach To String Formatting
- Changes To Exceptions
- Miscellaneous Other Changes
- Build and C API Changes
- 性能
- Porting To Python 3.0
- What's New in Python 2.7
- The Future for Python 2.x
- Changes to the Handling of Deprecation Warnings
- Python 3.1 Features
- PEP 372: Adding an Ordered Dictionary to collections
- PEP 378: Format Specifier for Thousands Separator
- PEP 389: The argparse Module for Parsing Command Lines
- PEP 391: Dictionary-Based Configuration For Logging
- PEP 3106: Dictionary Views
- PEP 3137: The memoryview Object
- 其他語言特性修改
- New and Improved Modules
- Build and C API Changes
- Other Changes and Fixes
- Porting to Python 2.7
- New Features Added to Python 2.7 Maintenance Releases
- Acknowledgements
- Python 2.6 有什么新變化
- Python 3.0
- Changes to the Development Process
- PEP 343: The 'with' statement
- PEP 366: Explicit Relative Imports From a Main Module
- PEP 370: Per-user site-packages Directory
- PEP 371: The multiprocessing Package
- PEP 3101: Advanced String Formatting
- PEP 3105: print As a Function
- PEP 3110: Exception-Handling Changes
- PEP 3112: Byte Literals
- PEP 3116: New I/O Library
- PEP 3118: Revised Buffer Protocol
- PEP 3119: Abstract Base Classes
- PEP 3127: Integer Literal Support and Syntax
- PEP 3129: Class Decorators
- PEP 3141: A Type Hierarchy for Numbers
- 其他語言特性修改
- New and Improved Modules
- Deprecations and Removals
- Build and C API Changes
- Porting to Python 2.6
- Acknowledgements
- What's New in Python 2.5
- PEP 308: Conditional Expressions
- PEP 309: Partial Function Application
- PEP 314: Metadata for Python Software Packages v1.1
- PEP 328: Absolute and Relative Imports
- PEP 338: Executing Modules as Scripts
- PEP 341: Unified try/except/finally
- PEP 342: New Generator Features
- PEP 343: The 'with' statement
- PEP 352: Exceptions as New-Style Classes
- PEP 353: Using ssize_t as the index type
- PEP 357: The 'index' method
- 其他語言特性修改
- New, Improved, and Removed Modules
- Build and C API Changes
- Porting to Python 2.5
- Acknowledgements
- What's New in Python 2.4
- PEP 218: Built-In Set Objects
- PEP 237: Unifying Long Integers and Integers
- PEP 289: Generator Expressions
- PEP 292: Simpler String Substitutions
- PEP 318: Decorators for Functions and Methods
- PEP 322: Reverse Iteration
- PEP 324: New subprocess Module
- PEP 327: Decimal Data Type
- PEP 328: Multi-line Imports
- PEP 331: Locale-Independent Float/String Conversions
- 其他語言特性修改
- New, Improved, and Deprecated Modules
- Build and C API Changes
- Porting to Python 2.4
- Acknowledgements
- What's New in Python 2.3
- PEP 218: A Standard Set Datatype
- PEP 255: Simple Generators
- PEP 263: Source Code Encodings
- PEP 273: Importing Modules from ZIP Archives
- PEP 277: Unicode file name support for Windows NT
- PEP 278: Universal Newline Support
- PEP 279: enumerate()
- PEP 282: The logging Package
- PEP 285: A Boolean Type
- PEP 293: Codec Error Handling Callbacks
- PEP 301: Package Index and Metadata for Distutils
- PEP 302: New Import Hooks
- PEP 305: Comma-separated Files
- PEP 307: Pickle Enhancements
- Extended Slices
- 其他語言特性修改
- New, Improved, and Deprecated Modules
- Pymalloc: A Specialized Object Allocator
- Build and C API Changes
- Other Changes and Fixes
- Porting to Python 2.3
- Acknowledgements
- What's New in Python 2.2
- 概述
- PEPs 252 and 253: Type and Class Changes
- PEP 234: Iterators
- PEP 255: Simple Generators
- PEP 237: Unifying Long Integers and Integers
- PEP 238: Changing the Division Operator
- Unicode Changes
- PEP 227: Nested Scopes
- New and Improved Modules
- Interpreter Changes and Fixes
- Other Changes and Fixes
- Acknowledgements
- What's New in Python 2.1
- 概述
- PEP 227: Nested Scopes
- PEP 236: future Directives
- PEP 207: Rich Comparisons
- PEP 230: Warning Framework
- PEP 229: New Build System
- PEP 205: Weak References
- PEP 232: Function Attributes
- PEP 235: Importing Modules on Case-Insensitive Platforms
- PEP 217: Interactive Display Hook
- PEP 208: New Coercion Model
- PEP 241: Metadata in Python Packages
- New and Improved Modules
- Other Changes and Fixes
- Acknowledgements
- What's New in Python 2.0
- 概述
- What About Python 1.6?
- New Development Process
- Unicode
- 列表推導式
- Augmented Assignment
- 字符串的方法
- Garbage Collection of Cycles
- Other Core Changes
- Porting to 2.0
- Extending/Embedding Changes
- Distutils: Making Modules Easy to Install
- XML Modules
- Module changes
- New modules
- IDLE Improvements
- Deleted and Deprecated Modules
- Acknowledgements
- 更新日志
- Python 下一版
- Python 3.7.3 最終版
- Python 3.7.3 發布候選版 1
- Python 3.7.2 最終版
- Python 3.7.2 發布候選版 1
- Python 3.7.1 最終版
- Python 3.7.1 RC 2版本
- Python 3.7.1 發布候選版 1
- Python 3.7.0 正式版
- Python 3.7.0 release candidate 1
- Python 3.7.0 beta 5
- Python 3.7.0 beta 4
- Python 3.7.0 beta 3
- Python 3.7.0 beta 2
- Python 3.7.0 beta 1
- Python 3.7.0 alpha 4
- Python 3.7.0 alpha 3
- Python 3.7.0 alpha 2
- Python 3.7.0 alpha 1
- Python 3.6.6 final
- Python 3.6.6 RC 1
- Python 3.6.5 final
- Python 3.6.5 release candidate 1
- Python 3.6.4 final
- Python 3.6.4 release candidate 1
- Python 3.6.3 final
- Python 3.6.3 release candidate 1
- Python 3.6.2 final
- Python 3.6.2 release candidate 2
- Python 3.6.2 release candidate 1
- Python 3.6.1 final
- Python 3.6.1 release candidate 1
- Python 3.6.0 final
- Python 3.6.0 release candidate 2
- Python 3.6.0 release candidate 1
- Python 3.6.0 beta 4
- Python 3.6.0 beta 3
- Python 3.6.0 beta 2
- Python 3.6.0 beta 1
- Python 3.6.0 alpha 4
- Python 3.6.0 alpha 3
- Python 3.6.0 alpha 2
- Python 3.6.0 alpha 1
- Python 3.5.5 final
- Python 3.5.5 release candidate 1
- Python 3.5.4 final
- Python 3.5.4 release candidate 1
- Python 3.5.3 final
- Python 3.5.3 release candidate 1
- Python 3.5.2 final
- Python 3.5.2 release candidate 1
- Python 3.5.1 final
- Python 3.5.1 release candidate 1
- Python 3.5.0 final
- Python 3.5.0 release candidate 4
- Python 3.5.0 release candidate 3
- Python 3.5.0 release candidate 2
- Python 3.5.0 release candidate 1
- Python 3.5.0 beta 4
- Python 3.5.0 beta 3
- Python 3.5.0 beta 2
- Python 3.5.0 beta 1
- Python 3.5.0 alpha 4
- Python 3.5.0 alpha 3
- Python 3.5.0 alpha 2
- Python 3.5.0 alpha 1
- Python 教程
- 課前甜點
- 使用 Python 解釋器
- 調用解釋器
- 解釋器的運行環境
- Python 的非正式介紹
- Python 作為計算器使用
- 走向編程的第一步
- 其他流程控制工具
- if 語句
- for 語句
- range() 函數
- break 和 continue 語句,以及循環中的 else 子句
- pass 語句
- 定義函數
- 函數定義的更多形式
- 小插曲:編碼風格
- 數據結構
- 列表的更多特性
- del 語句
- 元組和序列
- 集合
- 字典
- 循環的技巧
- 深入條件控制
- 序列和其它類型的比較
- 模塊
- 有關模塊的更多信息
- 標準模塊
- dir() 函數
- 包
- 輸入輸出
- 更漂亮的輸出格式
- 讀寫文件
- 錯誤和異常
- 語法錯誤
- 異常
- 處理異常
- 拋出異常
- 用戶自定義異常
- 定義清理操作
- 預定義的清理操作
- 類
- 名稱和對象
- Python 作用域和命名空間
- 初探類
- 補充說明
- 繼承
- 私有變量
- 雜項說明
- 迭代器
- 生成器
- 生成器表達式
- 標準庫簡介
- 操作系統接口
- 文件通配符
- 命令行參數
- 錯誤輸出重定向和程序終止
- 字符串模式匹配
- 數學
- 互聯網訪問
- 日期和時間
- 數據壓縮
- 性能測量
- 質量控制
- 自帶電池
- 標準庫簡介 —— 第二部分
- 格式化輸出
- 模板
- 使用二進制數據記錄格式
- 多線程
- 日志
- 弱引用
- 用于操作列表的工具
- 十進制浮點運算
- 虛擬環境和包
- 概述
- 創建虛擬環境
- 使用pip管理包
- 接下來?
- 交互式編輯和編輯歷史
- Tab 補全和編輯歷史
- 默認交互式解釋器的替代品
- 浮點算術:爭議和限制
- 表示性錯誤
- 附錄
- 交互模式
- 安裝和使用 Python
- 命令行與環境
- 命令行
- 環境變量
- 在Unix平臺中使用Python
- 獲取最新版本的Python
- 構建Python
- 與Python相關的路徑和文件
- 雜項
- 編輯器和集成開發環境
- 在Windows上使用 Python
- 完整安裝程序
- Microsoft Store包
- nuget.org 安裝包
- 可嵌入的包
- 替代捆綁包
- 配置Python
- 適用于Windows的Python啟動器
- 查找模塊
- 附加模塊
- 在Windows上編譯Python
- 其他平臺
- 在蘋果系統上使用 Python
- 獲取和安裝 MacPython
- IDE
- 安裝額外的 Python 包
- Mac 上的圖形界面編程
- 在 Mac 上分發 Python 應用程序
- 其他資源
- Python 語言參考
- 概述
- 其他實現
- 標注
- 詞法分析
- 行結構
- 其他形符
- 標識符和關鍵字
- 字面值
- 運算符
- 分隔符
- 數據模型
- 對象、值與類型
- 標準類型層級結構
- 特殊方法名稱
- 協程
- 執行模型
- 程序的結構
- 命名與綁定
- 異常
- 導入系統
- importlib
- 包
- 搜索
- 加載
- 基于路徑的查找器
- 替換標準導入系統
- Package Relative Imports
- 有關 main 的特殊事項
- 開放問題項
- 參考文獻
- 表達式
- 算術轉換
- 原子
- 原型
- await 表達式
- 冪運算符
- 一元算術和位運算
- 二元算術運算符
- 移位運算
- 二元位運算
- 比較運算
- 布爾運算
- 條件表達式
- lambda 表達式
- 表達式列表
- 求值順序
- 運算符優先級
- 簡單語句
- 表達式語句
- 賦值語句
- assert 語句
- pass 語句
- del 語句
- return 語句
- yield 語句
- raise 語句
- break 語句
- continue 語句
- import 語句
- global 語句
- nonlocal 語句
- 復合語句
- if 語句
- while 語句
- for 語句
- try 語句
- with 語句
- 函數定義
- 類定義
- 協程
- 最高層級組件
- 完整的 Python 程序
- 文件輸入
- 交互式輸入
- 表達式輸入
- 完整的語法規范
- Python 標準庫
- 概述
- 可用性注釋
- 內置函數
- 內置常量
- 由 site 模塊添加的常量
- 內置類型
- 邏輯值檢測
- 布爾運算 — and, or, not
- 比較
- 數字類型 — int, float, complex
- 迭代器類型
- 序列類型 — list, tuple, range
- 文本序列類型 — str
- 二進制序列類型 — bytes, bytearray, memoryview
- 集合類型 — set, frozenset
- 映射類型 — dict
- 上下文管理器類型
- 其他內置類型
- 特殊屬性
- 內置異常
- 基類
- 具體異常
- 警告
- 異常層次結構
- 文本處理服務
- string — 常見的字符串操作
- re — 正則表達式操作
- 模塊 difflib 是一個計算差異的助手
- textwrap — Text wrapping and filling
- unicodedata — Unicode 數據庫
- stringprep — Internet String Preparation
- readline — GNU readline interface
- rlcompleter — GNU readline的完成函數
- 二進制數據服務
- struct — Interpret bytes as packed binary data
- codecs — Codec registry and base classes
- 數據類型
- datetime — 基礎日期/時間數據類型
- calendar — General calendar-related functions
- collections — 容器數據類型
- collections.abc — 容器的抽象基類
- heapq — 堆隊列算法
- bisect — Array bisection algorithm
- array — Efficient arrays of numeric values
- weakref — 弱引用
- types — Dynamic type creation and names for built-in types
- copy — 淺層 (shallow) 和深層 (deep) 復制操作
- pprint — 數據美化輸出
- reprlib — Alternate repr() implementation
- enum — Support for enumerations
- 數字和數學模塊
- numbers — 數字的抽象基類
- math — 數學函數
- cmath — Mathematical functions for complex numbers
- decimal — 十進制定點和浮點運算
- fractions — 分數
- random — 生成偽隨機數
- statistics — Mathematical statistics functions
- 函數式編程模塊
- itertools — 為高效循環而創建迭代器的函數
- functools — 高階函數和可調用對象上的操作
- operator — 標準運算符替代函數
- 文件和目錄訪問
- pathlib — 面向對象的文件系統路徑
- os.path — 常見路徑操作
- fileinput — Iterate over lines from multiple input streams
- stat — Interpreting stat() results
- filecmp — File and Directory Comparisons
- tempfile — Generate temporary files and directories
- glob — Unix style pathname pattern expansion
- fnmatch — Unix filename pattern matching
- linecache — Random access to text lines
- shutil — High-level file operations
- macpath — Mac OS 9 路徑操作函數
- 數據持久化
- pickle —— Python 對象序列化
- copyreg — Register pickle support functions
- shelve — Python object persistence
- marshal — Internal Python object serialization
- dbm — Interfaces to Unix “databases”
- sqlite3 — SQLite 數據庫 DB-API 2.0 接口模塊
- 數據壓縮和存檔
- zlib — 與 gzip 兼容的壓縮
- gzip — 對 gzip 格式的支持
- bz2 — 對 bzip2 壓縮算法的支持
- lzma — 用 LZMA 算法壓縮
- zipfile — 在 ZIP 歸檔中工作
- tarfile — Read and write tar archive files
- 文件格式
- csv — CSV 文件讀寫
- configparser — Configuration file parser
- netrc — netrc file processing
- xdrlib — Encode and decode XDR data
- plistlib — Generate and parse Mac OS X .plist files
- 加密服務
- hashlib — 安全哈希與消息摘要
- hmac — 基于密鑰的消息驗證
- secrets — Generate secure random numbers for managing secrets
- 通用操作系統服務
- os — 操作系統接口模塊
- io — 處理流的核心工具
- time — 時間的訪問和轉換
- argparse — 命令行選項、參數和子命令解析器
- getopt — C-style parser for command line options
- 模塊 logging — Python 的日志記錄工具
- logging.config — 日志記錄配置
- logging.handlers — Logging handlers
- getpass — 便攜式密碼輸入工具
- curses — 終端字符單元顯示的處理
- curses.textpad — Text input widget for curses programs
- curses.ascii — Utilities for ASCII characters
- curses.panel — A panel stack extension for curses
- platform — Access to underlying platform's identifying data
- errno — Standard errno system symbols
- ctypes — Python 的外部函數庫
- 并發執行
- threading — 基于線程的并行
- multiprocessing — 基于進程的并行
- concurrent 包
- concurrent.futures — 啟動并行任務
- subprocess — 子進程管理
- sched — 事件調度器
- queue — 一個同步的隊列類
- _thread — 底層多線程 API
- _dummy_thread — _thread 的替代模塊
- dummy_threading — 可直接替代 threading 模塊。
- contextvars — Context Variables
- Context Variables
- Manual Context Management
- asyncio support
- 網絡和進程間通信
- asyncio — 異步 I/O
- socket — 底層網絡接口
- ssl — TLS/SSL wrapper for socket objects
- select — Waiting for I/O completion
- selectors — 高級 I/O 復用庫
- asyncore — 異步socket處理器
- asynchat — 異步 socket 指令/響應 處理器
- signal — Set handlers for asynchronous events
- mmap — Memory-mapped file support
- 互聯網數據處理
- email — 電子郵件與 MIME 處理包
- json — JSON 編碼和解碼器
- mailcap — Mailcap file handling
- mailbox — Manipulate mailboxes in various formats
- mimetypes — Map filenames to MIME types
- base64 — Base16, Base32, Base64, Base85 數據編碼
- binhex — 對binhex4文件進行編碼和解碼
- binascii — 二進制和 ASCII 碼互轉
- quopri — Encode and decode MIME quoted-printable data
- uu — Encode and decode uuencode files
- 結構化標記處理工具
- html — 超文本標記語言支持
- html.parser — 簡單的 HTML 和 XHTML 解析器
- html.entities — HTML 一般實體的定義
- XML處理模塊
- xml.etree.ElementTree — The ElementTree XML API
- xml.dom — The Document Object Model API
- xml.dom.minidom — Minimal DOM implementation
- xml.dom.pulldom — Support for building partial DOM trees
- xml.sax — Support for SAX2 parsers
- xml.sax.handler — Base classes for SAX handlers
- xml.sax.saxutils — SAX Utilities
- xml.sax.xmlreader — Interface for XML parsers
- xml.parsers.expat — Fast XML parsing using Expat
- 互聯網協議和支持
- webbrowser — 方便的Web瀏覽器控制器
- cgi — Common Gateway Interface support
- cgitb — Traceback manager for CGI scripts
- wsgiref — WSGI Utilities and Reference Implementation
- urllib — URL 處理模塊
- urllib.request — 用于打開 URL 的可擴展庫
- urllib.response — Response classes used by urllib
- urllib.parse — Parse URLs into components
- urllib.error — Exception classes raised by urllib.request
- urllib.robotparser — Parser for robots.txt
- http — HTTP 模塊
- http.client — HTTP協議客戶端
- ftplib — FTP protocol client
- poplib — POP3 protocol client
- imaplib — IMAP4 protocol client
- nntplib — NNTP protocol client
- smtplib —SMTP協議客戶端
- smtpd — SMTP Server
- telnetlib — Telnet client
- uuid — UUID objects according to RFC 4122
- socketserver — A framework for network servers
- http.server — HTTP 服務器
- http.cookies — HTTP state management
- http.cookiejar — Cookie handling for HTTP clients
- xmlrpc — XMLRPC 服務端與客戶端模塊
- xmlrpc.client — XML-RPC client access
- xmlrpc.server — Basic XML-RPC servers
- ipaddress — IPv4/IPv6 manipulation library
- 多媒體服務
- audioop — Manipulate raw audio data
- aifc — Read and write AIFF and AIFC files
- sunau — 讀寫 Sun AU 文件
- wave — 讀寫WAV格式文件
- chunk — Read IFF chunked data
- colorsys — Conversions between color systems
- imghdr — 推測圖像類型
- sndhdr — 推測聲音文件的類型
- ossaudiodev — Access to OSS-compatible audio devices
- 國際化
- gettext — 多語種國際化服務
- locale — 國際化服務
- 程序框架
- turtle — 海龜繪圖
- cmd — 支持面向行的命令解釋器
- shlex — Simple lexical analysis
- Tk圖形用戶界面(GUI)
- tkinter — Tcl/Tk的Python接口
- tkinter.ttk — Tk themed widgets
- tkinter.tix — Extension widgets for Tk
- tkinter.scrolledtext — 滾動文字控件
- IDLE
- 其他圖形用戶界面(GUI)包
- 開發工具
- typing — 類型標注支持
- pydoc — Documentation generator and online help system
- doctest — Test interactive Python examples
- unittest — 單元測試框架
- unittest.mock — mock object library
- unittest.mock 上手指南
- 2to3 - 自動將 Python 2 代碼轉為 Python 3 代碼
- test — Regression tests package for Python
- test.support — Utilities for the Python test suite
- test.support.script_helper — Utilities for the Python execution tests
- 調試和分析
- bdb — Debugger framework
- faulthandler — Dump the Python traceback
- pdb — The Python Debugger
- The Python Profilers
- timeit — 測量小代碼片段的執行時間
- trace — Trace or track Python statement execution
- tracemalloc — Trace memory allocations
- 軟件打包和分發
- distutils — 構建和安裝 Python 模塊
- ensurepip — Bootstrapping the pip installer
- venv — 創建虛擬環境
- zipapp — Manage executable Python zip archives
- Python運行時服務
- sys — 系統相關的參數和函數
- sysconfig — Provide access to Python's configuration information
- builtins — 內建對象
- main — 頂層腳本環境
- warnings — Warning control
- dataclasses — 數據類
- contextlib — Utilities for with-statement contexts
- abc — 抽象基類
- atexit — 退出處理器
- traceback — Print or retrieve a stack traceback
- future — Future 語句定義
- gc — 垃圾回收器接口
- inspect — 檢查對象
- site — Site-specific configuration hook
- 自定義 Python 解釋器
- code — Interpreter base classes
- codeop — Compile Python code
- 導入模塊
- zipimport — Import modules from Zip archives
- pkgutil — Package extension utility
- modulefinder — 查找腳本使用的模塊
- runpy — Locating and executing Python modules
- importlib — The implementation of import
- Python 語言服務
- parser — Access Python parse trees
- ast — 抽象語法樹
- symtable — Access to the compiler's symbol tables
- symbol — 與 Python 解析樹一起使用的常量
- token — 與Python解析樹一起使用的常量
- keyword — 檢驗Python關鍵字
- tokenize — Tokenizer for Python source
- tabnanny — 模糊縮進檢測
- pyclbr — Python class browser support
- py_compile — Compile Python source files
- compileall — Byte-compile Python libraries
- dis — Python 字節碼反匯編器
- pickletools — Tools for pickle developers
- 雜項服務
- formatter — Generic output formatting
- Windows系統相關模塊
- msilib — Read and write Microsoft Installer files
- msvcrt — Useful routines from the MS VC++ runtime
- winreg — Windows 注冊表訪問
- winsound — Sound-playing interface for Windows
- Unix 專有服務
- posix — The most common POSIX system calls
- pwd — 用戶密碼數據庫
- spwd — The shadow password database
- grp — The group database
- crypt — Function to check Unix passwords
- termios — POSIX style tty control
- tty — 終端控制功能
- pty — Pseudo-terminal utilities
- fcntl — The fcntl and ioctl system calls
- pipes — Interface to shell pipelines
- resource — Resource usage information
- nis — Interface to Sun's NIS (Yellow Pages)
- Unix syslog 庫例程
- 被取代的模塊
- optparse — Parser for command line options
- imp — Access the import internals
- 未創建文檔的模塊
- 平臺特定模塊
- 擴展和嵌入 Python 解釋器
- 推薦的第三方工具
- 不使用第三方工具創建擴展
- 使用 C 或 C++ 擴展 Python
- 自定義擴展類型:教程
- 定義擴展類型:已分類主題
- 構建C/C++擴展
- 在Windows平臺編譯C和C++擴展
- 在更大的應用程序中嵌入 CPython 運行時
- Embedding Python in Another Application
- Python/C API 參考手冊
- 概述
- 代碼標準
- 包含文件
- 有用的宏
- 對象、類型和引用計數
- 異常
- 嵌入Python
- 調試構建
- 穩定的應用程序二進制接口
- The Very High Level Layer
- Reference Counting
- 異常處理
- Printing and clearing
- 拋出異常
- Issuing warnings
- Querying the error indicator
- Signal Handling
- Exception Classes
- Exception Objects
- Unicode Exception Objects
- Recursion Control
- 標準異常
- 標準警告類別
- 工具
- 操作系統實用程序
- 系統功能
- 過程控制
- 導入模塊
- Data marshalling support
- 語句解釋及變量編譯
- 字符串轉換與格式化
- 反射
- 編解碼器注冊與支持功能
- 抽象對象層
- Object Protocol
- 數字協議
- Sequence Protocol
- Mapping Protocol
- 迭代器協議
- 緩沖協議
- Old Buffer Protocol
- 具體的對象層
- 基本對象
- 數值對象
- 序列對象
- 容器對象
- 函數對象
- 其他對象
- Initialization, Finalization, and Threads
- 在Python初始化之前
- 全局配置變量
- Initializing and finalizing the interpreter
- Process-wide parameters
- Thread State and the Global Interpreter Lock
- Sub-interpreter support
- Asynchronous Notifications
- Profiling and Tracing
- Advanced Debugger Support
- Thread Local Storage Support
- 內存管理
- 概述
- 原始內存接口
- Memory Interface
- 對象分配器
- 默認內存分配器
- Customize Memory Allocators
- The pymalloc allocator
- tracemalloc C API
- 示例
- 對象實現支持
- 在堆中分配對象
- Common Object Structures
- Type 對象
- Number Object Structures
- Mapping Object Structures
- Sequence Object Structures
- Buffer Object Structures
- Async Object Structures
- 使對象類型支持循環垃圾回收
- API 和 ABI 版本管理
- 分發 Python 模塊
- 關鍵術語
- 開源許可與協作
- 安裝工具
- 閱讀指南
- 我該如何...?
- ...為我的項目選擇一個名字?
- ...創建和分發二進制擴展?
- 安裝 Python 模塊
- 關鍵術語
- 基本使用
- 我應如何 ...?
- ... 在 Python 3.4 之前的 Python 版本中安裝 pip ?
- ... 只為當前用戶安裝軟件包?
- ... 安裝科學計算類 Python 軟件包?
- ... 使用并行安裝的多個 Python 版本?
- 常見的安裝問題
- 在 Linux 的系統 Python 版本上安裝
- 未安裝 pip
- 安裝二進制編譯擴展
- Python 常用指引
- 將 Python 2 代碼遷移到 Python 3
- 簡要說明
- 詳情
- 將擴展模塊移植到 Python 3
- 條件編譯
- 對象API的更改
- 模塊初始化和狀態
- CObject 替換為 Capsule
- 其他選項
- Curses Programming with Python
- What is curses?
- Starting and ending a curses application
- Windows and Pads
- Displaying Text
- User Input
- For More Information
- 實現描述器
- 摘要
- 定義和簡介
- 描述器協議
- 發起調用描述符
- 描述符示例
- Properties
- 函數和方法
- Static Methods and Class Methods
- 函數式編程指引
- 概述
- 迭代器
- 生成器表達式和列表推導式
- 生成器
- 內置函數
- itertools 模塊
- The functools module
- Small functions and the lambda expression
- Revision History and Acknowledgements
- 引用文獻
- 日志 HOWTO
- 日志基礎教程
- 進階日志教程
- 日志級別
- 有用的處理程序
- 記錄日志中引發的異常
- 使用任意對象作為消息
- 優化
- 日志操作手冊
- 在多個模塊中使用日志
- 在多線程中使用日志
- 使用多個日志處理器和多種格式化
- 在多個地方記錄日志
- 日志服務器配置示例
- 處理日志處理器的阻塞
- Sending and receiving logging events across a network
- Adding contextual information to your logging output
- Logging to a single file from multiple processes
- Using file rotation
- Use of alternative formatting styles
- Customizing LogRecord
- Subclassing QueueHandler - a ZeroMQ example
- Subclassing QueueListener - a ZeroMQ example
- An example dictionary-based configuration
- Using a rotator and namer to customize log rotation processing
- A more elaborate multiprocessing example
- Inserting a BOM into messages sent to a SysLogHandler
- Implementing structured logging
- Customizing handlers with dictConfig()
- Using particular formatting styles throughout your application
- Configuring filters with dictConfig()
- Customized exception formatting
- Speaking logging messages
- Buffering logging messages and outputting them conditionally
- Formatting times using UTC (GMT) via configuration
- Using a context manager for selective logging
- 正則表達式HOWTO
- 概述
- 簡單模式
- 使用正則表達式
- 更多模式能力
- 修改字符串
- 常見問題
- 反饋
- 套接字編程指南
- 套接字
- 創建套接字
- 使用一個套接字
- 斷開連接
- 非阻塞的套接字
- 排序指南
- 基本排序
- 關鍵函數
- Operator 模塊函數
- 升序和降序
- 排序穩定性和排序復雜度
- 使用裝飾-排序-去裝飾的舊方法
- 使用 cmp 參數的舊方法
- 其它
- Unicode 指南
- Unicode 概述
- Python's Unicode Support
- Reading and Writing Unicode Data
- Acknowledgements
- 如何使用urllib包獲取網絡資源
- 概述
- Fetching URLs
- 處理異常
- info and geturl
- Openers and Handlers
- Basic Authentication
- Proxies
- Sockets and Layers
- 腳注
- Argparse 教程
- 概念
- 基礎
- 位置參數介紹
- Introducing Optional arguments
- Combining Positional and Optional arguments
- Getting a little more advanced
- Conclusion
- ipaddress模塊介紹
- 創建 Address/Network/Interface 對象
- 審查 Address/Network/Interface 對象
- Network 作為 Address 列表
- 比較
- 將IP地址與其他模塊一起使用
- 實例創建失敗時獲取更多詳細信息
- Argument Clinic How-To
- The Goals Of Argument Clinic
- Basic Concepts And Usage
- Converting Your First Function
- Advanced Topics
- 使用 DTrace 和 SystemTap 檢測CPython
- Enabling the static markers
- Static DTrace probes
- Static SystemTap markers
- Available static markers
- SystemTap Tapsets
- 示例
- Python 常見問題
- Python常見問題
- 一般信息
- 現實世界中的 Python
- 編程常見問題
- 一般問題
- 核心語言
- 數字和字符串
- 性能
- 序列(元組/列表)
- 對象
- 模塊
- 設計和歷史常見問題
- 為什么Python使用縮進來分組語句?
- 為什么簡單的算術運算得到奇怪的結果?
- 為什么浮點計算不準確?
- 為什么Python字符串是不可變的?
- 為什么必須在方法定義和調用中顯式使用“self”?
- 為什么不能在表達式中賦值?
- 為什么Python對某些功能(例如list.index())使用方法來實現,而其他功能(例如len(List))使用函數實現?
- 為什么 join()是一個字符串方法而不是列表或元組方法?
- 異常有多快?
- 為什么Python中沒有switch或case語句?
- 難道不能在解釋器中模擬線程,而非得依賴特定于操作系統的線程實現嗎?
- 為什么lambda表達式不能包含語句?
- 可以將Python編譯為機器代碼,C或其他語言嗎?
- Python如何管理內存?
- 為什么CPython不使用更傳統的垃圾回收方案?
- CPython退出時為什么不釋放所有內存?
- 為什么有單獨的元組和列表數據類型?
- 列表是如何在CPython中實現的?
- 字典是如何在CPython中實現的?
- 為什么字典key必須是不可變的?
- 為什么 list.sort() 沒有返回排序列表?
- 如何在Python中指定和實施接口規范?
- 為什么沒有goto?
- 為什么原始字符串(r-strings)不能以反斜杠結尾?
- 為什么Python沒有屬性賦值的“with”語句?
- 為什么 if/while/def/class語句需要冒號?
- 為什么Python在列表和元組的末尾允許使用逗號?
- 代碼庫和插件 FAQ
- 通用的代碼庫問題
- 通用任務
- 線程相關
- 輸入輸出
- 網絡 / Internet 編程
- 數據庫
- 數學和數字
- 擴展/嵌入常見問題
- 可以使用C語言中創建自己的函數嗎?
- 可以使用C++語言中創建自己的函數嗎?
- C很難寫,有沒有其他選擇?
- 如何從C執行任意Python語句?
- 如何從C中評估任意Python表達式?
- 如何從Python對象中提取C的值?
- 如何使用Py_BuildValue()創建任意長度的元組?
- 如何從C調用對象的方法?
- 如何捕獲PyErr_Print()(或打印到stdout / stderr的任何內容)的輸出?
- 如何從C訪問用Python編寫的模塊?
- 如何從Python接口到C ++對象?
- 我使用Setup文件添加了一個模塊,為什么make失敗了?
- 如何調試擴展?
- 我想在Linux系統上編譯一個Python模塊,但是缺少一些文件。為什么?
- 如何區分“輸入不完整”和“輸入無效”?
- 如何找到未定義的g++符號__builtin_new或__pure_virtual?
- 能否創建一個對象類,其中部分方法在C中實現,而其他方法在Python中實現(例如通過繼承)?
- Python在Windows上的常見問題
- 我怎樣在Windows下運行一個Python程序?
- 我怎么讓 Python 腳本可執行?
- 為什么有時候 Python 程序會啟動緩慢?
- 我怎樣使用Python腳本制作可執行文件?
- *.pyd 文件和DLL文件相同嗎?
- 我怎樣將Python嵌入一個Windows程序?
- 如何讓編輯器不要在我的 Python 源代碼中插入 tab ?
- 如何在不阻塞的情況下檢查按鍵?
- 圖形用戶界面(GUI)常見問題
- 圖形界面常見問題
- Python 是否有平臺無關的圖形界面工具包?
- 有哪些Python的GUI工具是某個平臺專用的?
- 有關Tkinter的問題
- “為什么我的電腦上安裝了 Python ?”
- 什么是Python?
- 為什么我的電腦上安裝了 Python ?
- 我能刪除 Python 嗎?
- 術語對照表
- 文檔說明
- Python 文檔貢獻者
- 解決 Bug
- 文檔錯誤
- 使用 Python 的錯誤追蹤系統
- 開始為 Python 貢獻您的知識
- 版權
- 歷史和許可證
- 軟件歷史
- 訪問Python或以其他方式使用Python的條款和條件
- Python 3.7.3 的 PSF 許可協議
- Python 2.0 的 BeOpen.com 許可協議
- Python 1.6.1 的 CNRI 許可協議
- Python 0.9.0 至 1.2 的 CWI 許可協議
- 集成軟件的許可和認可
- Mersenne Twister
- 套接字
- Asynchronous socket services
- Cookie management
- Execution tracing
- UUencode and UUdecode functions
- XML Remote Procedure Calls
- test_epoll
- Select kqueue
- SipHash24
- strtod and dtoa
- OpenSSL
- expat
- libffi
- zlib
- cfuhash
- libmpdec