### 导航
- [索引](../genindex.xhtml "总目录")
- [模块](../py-modindex.xhtml "Python 模块索引") |
- [下一页](newtypes_tutorial.xhtml "2. 自定义扩展类型:教程") |
- [上一页](index.xhtml "扩展和嵌入 Python 解释器") |
- ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png)
- [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 解释器") |
- ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png)
- [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