>>> 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 可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,你 必须 先包含 Python.h
。
推荐总是在 Python.h
前定义 PY_SSIZE_T_CLEAN
。查看 提取扩展函数的参数 来了解这个宏的更多内容。
所有在 Python.h
中定义的用户可见的符号都具有 Py
或 PY
前缀,已在标准头文件中定义的那些除外。 考虑到便利性,也由于其在 Python 解释器中被广泛使用,"Python.h"
还包含了一些标准头文件: <stdio.h>
,<string.h>
,<errno.h>
和 <stdlib.h>
。 如果后面的头文件在你的系统上不存在,它还会直接声明函数 malloc()
,free()
和 realloc()
。
下面添加C函数到扩展模块,当调用 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 值。 它使用模板字符串确定需要的参数类型以及存储被转换的值的 C 变量类型。 细节将稍后说明。
PyArg_ParseTuple()
在所有参数都有正确类型且组成部分按顺序放在传递进来的地址里时,返回真(非零)。其在传入无效参数时返回假(零)。在后续例子里,还会抛出特定异常,使得调用的函数可以理解返回 NULL
(也就是例子里所见)。
1.2. 关于错误和异常
整个 Python 解释器系统有一个如下所述的重要惯例:当一个函数运行失败时,它应当设置一个异常条件并返回一个错误值(通常为 -1
或 NULL
指针)。 异常信息保存在解释器线程状态的三个成员中。 如果没有异常则它们的值为 NULL
。 在其他情况下它们是 sys.exc_info()
所返回的 Python 元组的成员的 C 对应物。 它们分别是异常类型、异常实例和回溯对象。 理解它们对于理解错误是如何被传递的非常重要。
Python API中定义了一些函数来设置这些变量。
最常用的就是 PyErr_SetString()
。 其参数是异常对象和 C 字符串。 异常对象一般是像 PyExc_ZeroDivisionError
这样的预定义对象。 C 字符串指明异常原因,并被转换为一个 Python 字符串对象存储为异常的“关联值”。
另一个有用的函数是 PyErr_SetFromErrno()
,仅接受一个异常对象,异常描述包含在全局变量 errno
中。最通用的函数还是 PyErr_SetObject()
,包含两个参数,分别为异常对象和异常描述。你不需要使用 Py_INCREF()
来增加传递到其他函数的参数对象的引用计数。
你可以通过 PyErr_Occurred()
在不造成破坏的情况下检测是否设置了异常。 这将返回当前异常对象,或者如果未发生异常则返回 NULL
。 你通常不需要调用 PyErr_Occurred()
来查看函数调用中是否发生了错误,因为你应该能从返回值中看出来。
当一个函数 f 调用另一个函数 g 时检测到后者出错了,f 应当自己返回一个错误值 (通常为 NULL
或 -1
)。 它 不应 调用某个 PyErr_*
函数 --- 这类函数已经被 g 调用过了。 f 的调用者随后也应当返回一个错误来提示 它的 调用者,同样 不应 调用 PyErr_*
,依此类推 --- 错误的最详细原因已经由首先检测到它的函数报告了。 一旦这个错误到达 Python 解释器的主循环,它会中止当前执行的 Python 代码并尝试找出由 Python 程序员所指定的异常处理句柄。
(在某些情况下模块确实能够通过调用其它 PyErr_*
函数来给出更为详细的错误消息,并且在这些情况下是可以这样做的。 但是按照一般规则,这是不必要的,并可能导致有关错误的信息丢失:大多数操作会由于种种原因而失败。)
想要忽略由一个失败的函数调用所设置的异常,异常条件必须通过调用 PyErr_Clear()
显式地被清除。 C 代码应当调用 PyErr_Clear()
的唯一情况是如果它不想将错误传给解释器而是想完全由自己来处理它(可能是尝试其他方法,或是假装没有出错)。
每次失败的 malloc()
调用必须转换为一个异常。 malloc()
(或 realloc()
)的直接调用者必须调用 PyErr_NoMemory()
来返回错误来提示。所有对象创建函数(例如 PyLong_FromLong()
)已经这么做了,所以这个提示仅用于直接调用 malloc()
的情况。
还要注意的是,除了 PyArg_ParseTuple()
等重要的例外,返回整数状态码的函数通常都是返回正值或零来表示成功,而以 -1
表示失败,如同 Unix 系统调用一样。
最后,当你返回一个错误指示器时要注意清理垃圾(通过为你已经创建的对象执行 Py_XDECREF()
或 Py_DECREF()
调用)!
选择引发哪个异常完全取决于你的喜好。 所有内置的 Python 异常都有对应的预声明 C 对象,例如 PyExc_ZeroDivisionError
,你可以直接使用它们。 当然,你应当明智地选择异常 --- 不要使用 PyExc_TypeError
来表示一个文件无法被打开 (那大概应该用 PyExc_IOError
)。 如果参数列表有问题,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_XINCREF(SpamError);
if (PyModule_AddObject(m, "error", SpamError) < 0) {
Py_XDECREF(SpamError);
Py_CLEAR(SpamError);
Py_DECREF(m);
return NULL;
return m;
注意异常对象的Python名字是 spam.error
。而 PyErr_NewException()
函数可以创建一个类,其基类为 Exception
(除非是另一个类传入以替换 NULL
), 细节参见 內建的例外 。
同样注意的是创建类保存了 SpamError
的一个引用,这是有意的。为了防止被垃圾回收掉,否则 SpamError
随时会成为野指针。
一会讨论 PyMODINIT_FUNC
作为函数返回类型的用法。
spam.error
异常可以在扩展模块中抛出,通过 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);
如果在参数列表中检测到错误,它将返回 NULL
(该值是返回对象指针的函数所使用的错误提示),这取决于 PyArg_ParseTuple()
设置的异常。 在其他情况下参数的字符串值会被拷贝到局部变量 command
。 这是一个指针赋值并且你不应该修改它所指向的字符串 (因此在标准 C 中,变量 command
应当被正确地声明为 const char *command
)。
下一个语句使用UNIX系统函数 system()
,传递给他的参数是刚才从 PyArg_ParseTuple()
取出的:
sts = system(command);
我们的 spam.system()
函数必须返回 sts
的值作为Python对象。这通过使用函数 PyLong_FromLong()
来实现。
return PyLong_FromLong(sts);
在这种情况下,会返回一个整数对象,(这个对象会在Python堆里面管理)。
如果你有一个不返回有用参数的 C 函数(即返回 void
的函数),则对应的 Python 函数必须返回 None
。 你必须使用这种写法(它是通过 Py_RETURN_NONE
宏来实现的)
Py_INCREF(Py_None);
return Py_None;
Py_None
是特殊 Python 对象 None
所对应的 C 名称。 它是一个真正的 Python 对象而不是 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()
的陈旧变量。
如果单独使用 METH_VARARGS
,函数会等待Python传来tuple格式的参数,并最终使用 PyArg_ParseTuple()
进行解析。
METH_KEYWORDS
值表示接受关键字参数。这种情况下C函数需要接受第三个 PyObject *
对象,表示字典参数,使用 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()
,该函数会返回一个模块对象,并基于在模块定义中找到的表将内置函数对象插入到新创建的模块中(该表是一个 PyMethodDef
结构体的数组)。 PyModule_Create()
返回一个指向它所创建的模块对象的指针。 它可能会因程度严重的特定错误而中止,或者在模块无法成功初始化时返回 NULL
。 初始化函数必须返回模块对象给其调用者,这样它就可以被插入到 sys.modules
中。
当嵌入Python时, PyInit_spam()
函数不会被自动调用,除非放在 PyImport_Inittab
表里。要添加模块到初始化表,使用 PyImport_AppendInittab()
,可选的跟着一个模块的导入。
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 */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
Py_Initialize();
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
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 的多阶段初始化。
1.5. 编译和链接
在你能使用你的新写的扩展之前,你还需要做两件事情:使用 Python 系统来编译和链接。如果你使用动态加载,这取决于你使用的操作系统的动态加载机制;更多信息请参考编译扩展模块的章节( 构建C/C++扩展 章节),以及在 Windows 上编译需要的额外信息( 在 Windows 上构建 C 和 C++ 扩展 章节)。
如果你不使用动态加载,或者想要让模块永久性的作为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
命令行代码。)
调用Python函数很简单,首先Python程序要传递Python函数对象。应该提供个函数(或其他接口)来实现。当调用这个函数时,用全局变量保存Python函数对象的指针,还要调用 (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
标志注册到解释器,这在 模块方法表和初始化函数 章节会描述。 PyArg_ParseTuple()
函数及其参数的文档在 提取扩展函数的参数 。
Py_XINCREF()
和 Py_XDECREF()
这两个宏可增加/减少一个对象的引用计数,并且当存在 NULL
指针时仍可保证安全 (但请注意在这个上下文中 temp 将不为 NULL
)。 更多相关信息请参考 引用计数 章节。
随后,当要调用此函数时,你将调用 C 函数 PyObject_CallObject()
。 该函数有两个参数,它们都属于指针,指向任意 Python 对象:即 Python 函数,及其参数列表。 参数列表必须总是一个元组对象,其长度即参数的个数量。 要不带参数地调用 Python 函数,则传入 NULL
或一个空元组;要带一个参数调用它,则传入一个单元组。 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()
返回Python对象指针,这也是Python函数的返回值。 PyObject_CallObject()
是一个对其参数 "引用计数无关" 的函数。例子中新的元组创建用于参数列表,并且在 PyObject_CallObject()
之后立即使用了 Py_DECREF()
。
PyObject_CallObject()
的返回值总是“新”的:要么是一个新建的对象;要么是已有对象,但增加了引用计数。所以除非你想把结果保存在全局变量中,你需要对这个值使用 Py_DECREF()
,即使你对里面的内容(特别!)不感兴趣。
但是在你这么做之前,很重要的一点是检查返回值不是 NULL
。 如果是的话,Python 函数会终止并引发异常。 如果调用 PyObject_CallObject()
的 C 代码是在 Python 中发起调用的,它应当立即返回一个错误来告知其 Python 调用者,以便解释器能打印栈回溯信息,或者让调用方 Python 代码能处理该异常。 如果这无法做到或不合本意,则应当通过调用 PyErr_Clear()
来清除异常。 例如:
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
依赖于具体的回调函数,你还要提供一个参数列表到 PyObject_CallObject()
。在某些情况下参数列表是由Python程序提供的,通过接口再传到回调函数对象。这样就可以不改变形式直接传递。另外一些时候你要构造一个新的元组来传递参数。最简单的方法就是 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()
可能会遭遇内存不足等等。
当你调用函数时还需要注意,用关键字参数调用 PyObject_Call()
,需要支持普通参数和关键字参数。有如如上例子中,我们使用 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()
的声明如下:
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
参数 arg 必须是一个元组对象,包含从 Python 传递给 C 函数的参数列表。format 参数必须是一个格式字符串,语法请参考 Python C/API 手册中的 解析参数并构建值变量。剩余参数是各个变量的地址,类型要与格式字符串对应。
注意 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)",