使用C给Python写扩展

Python 扩展

Python 和 C 粘合性很好,用 C 给 Python 添加扩展一般是性能问题,另外就是想要加密部分可以编译成二进制,保证源码不会泄露。

C Extending Moudle

C 扩展大概就是使用 Python 提供的方法将 C 代码在封装一遍。Python 提供了 Python.h 头文件,使用这些接口将 C 进行包装,最后编译成 so 文件(在windows下是dll)

编写 C 扩展需要三部分,缺一不可。

  • 导出函数
  • 方法列表
  • 初始化函数

Example

最主要的是先封装C函数,接下来举个例子,我的系统是 Ubuntu 14.04,Python2.7

定义函数

比方用 C 写一个两数相加的方法 plus,文件名为 sample.c

1
2
3
4
int plus(int n, int m)
{
return n + m;
}

导出函数

将该方法使用 Python 提供的API封装,首先导入头文件 Python.h

1
2
3
4
5
6
7
8
9
10
#include <Python.h>

static PyObject* wrap_plus(PyObject* self, PyObject* args)
{
int n, m, result;
if (!PyArg_ParseTuple(args, "ii:plus", &n, &m))
return NULL;
result = plus(n, m);
return Py_BuildValue("i", result);
}

所有的导出函数都返回一个 PyObject 指针,如果对应的C函数没有真正的返回值(即返回值类型为void),则应返回一个全局的 None 对象(Py_None),并将其引用计数增 1

1
2
3
4
5
PyObject* method(PyObject *self, PyObject *args)
{
Py_INCREF(Py_None);
return Py_None;
}
  • PyObject 是一个能表示任何Python对象的C数据类型
  • PyArg_ParseTuple() 函数被用来将Python中的值转换成C中对应表示
  • Py_BuildValue() 函数被用来根据C数据类型创建Python对象
  • 更多接口查看文档

PyArg_ParseTuple 更多类型请看文档, ii,表示args的长度必须为2,且必须为int类型

方法列表

1
2
3
4
static PyMethodDef sampleMethods[] = {
{"plus", wrap_plus, METH_VARARGS, "Return n plus m"},
{NULL, NULL}
}
  • 方法列表中的每项由四个部分组成:方法名、导出函数、参数传递方式和方法描述
  • 方法名是从Python解释器中调用该方法时所使用的名字
  • 参数传递方式则规定了Python向C函数传递参数的具体形式,可选的两种方式是METH_VARARGS和METH_KEYWORDS
  • {NULL, NULL} 标识结束

初始化函数

1
2
3
4
5
void initsample()
{
PyObject* m;
m = Py_InitModule("sample", sampleMethods);
}

构造 module 也可以使用 PyModuleDef_HEAD_INIT 和 PyModule_Create(官方这么写的)

编译链接

gcc -fpic -c -I/usr/include/python2.7 -I /usr/lib/python2.7/config-x86_64-linux-gnu sample.c gcc -shared -o sample.so sample.o cp sample.so /python/python/site-packages/

或者使用 distutils 安装

参考