1. Python的C扩展
其实只要你懂得C语言编程,给Python添加新的内置(build-in)模块将十分容易。这些扩展(extension)模块可以实现两种无法直接在Python中进行的操作:他们可以实现新的内置对象类型,以及可以调用C语言的库函数和进行系统调用。
为了支持扩展,Python API定义了一个函数(functions)、宏命(macros)令和变量(variables)的集合,该集合提供了对Python运行时(run-time)系统的多方面的访问。Python API可以通过包含头文件Python.h
的方式,整合进C语言源文件中。
注意: C语言的扩展接口指的是CPython,且扩展模块在其他Python实现上无效。在许多情况下,可以避免编写C扩展,并保留可移植性到其他实现上。例如,如果你的用例调用C库函数或进行系统调用,你可以考虑使用
ctypes
模块或cffi库而不是编写自定义的C代码。这些模块允许您编写Python代码来与C代码进行对接,并且Python实现比编写和编译C扩展模块更方便。
A Simple Example
我们准备构建一个名为spam
的扩展模块,并假设我们想要为C库函数system()
创建一个Python接口。该函数以null结尾(null-terminated)的字符串作为参数并返回一个整数。我们希望这个函数可以通过下列形式在Python中进行调用:
import spam
status = spam.system("ls -l")
我们首先新建一个spammodule.c
文件。(一般来说,如果一个模块的名字叫spam
,则实现它的C语言文件应该命名为spammodule.c;如果模块的名字非常长,就像
spammify,那对应的文件名就可以直接写为
spammify.c`。)
该文件的第一行为:
#include <Python.h>
这可以获取Python API。
注意:因为Python会定义一些预处理器的定义,这些定义会影响某些系统的标准头文件,所以你必须在文件的一开始包含
Python.h
文件。
然后添加函数的主体代码:
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);
}
其中PyArg_ParseTuple(args, “s”, &command)检测参数列表是否存在错误,错误则返回NULL
,否则把指令传给command
。
现在相当于我们已经实现了spam_system
函数,接下来我们就要讨论如何让Python能调用它。
在这里我们的模块名称为spam
,而spam_system
是它的方法。所以我们需要把spam_system
添加进spam
的方法列表中。
static PyMethodDef SpamMethods[] = {
...
{
"system", spam_system, METH_VARARGS,
"Execute a shell command."},
...
{NULL, NULL, 0, NULL} /* Sentinel */
};
其中,METH_VARARGS
是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数。若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递。system
是函数spam_system
在模块中的名字。
现在方法有了,我们需要构建一个表征该模块的结构体,然后将方法列表添加进去。如下所示:
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
};
定义完结构体后,就要定义初始化函数了。如下所示:
PyMODINIT_FUNC PyInit_spam(void)
{
return PyModule_Create(&spammodule);
}
注意:在初始化函数中,一定要传入模块结构体。用
PyModule_Create()
函数初始化模块结构体。并且在Python 3中,初始化函数一定要用PyInit_name()
的方式命名。初始化函数没有添加static声明,所以模块的初始化函数是模块的唯一对外接口。
上述材料都准备