一般来说,Python扩展C,有三种方式,来实现加速,以下通过Python2.7,minw32 3.82.90,windows 7 进行说明。
一、ctypes
ctypes使用c函数,需要先将c编译成动态链接库,即.dll文件。
举个简单的栗子,c文件 examples.c:
#include <stdio.h>
int show_me_the_money(int m) {
printf("Money is %d\n", m);
return m;
编译成动态链接库:
mingw32-gcc examples.c -o examples.dll -shared通过ctypes调用show_me_the_money这个函数:
#coding: utf-8
import ctypes
lib = ctypes.cdll.LoadLibrary("examples.dll")
lib.show_me_the_money(100)
结果是:
Money is 100
ctypes方式简单易用,只要动态库不涉及线程操作,交互的数据结构不复杂,特别是动态链接库已经有了,推荐使用。ctypes的详细细节可参考官方文档。
二、c extension方式
python c extension也是一种动态链接库,与一般的dll文件相比,它有两点不同
要实现一个PyMODINIT_FUNC函数,函数名称为 init模块名,比如你的模块名是pyTEST,那么这个函数名称就是 initpyTEST
文件后缀为.pyd
Enough talk, let's fight!
pyTEST.c:
#include <Python.h>
#include <Windows.h>
static PyObject *
get_version(PyObject *self, PyObject *args)
return Py_BuildValue("s", "1.0");
static PyMethodDef module_methods[] = {
{"get_version", get_version, METH_VARARGS, "Get module version"},
{NULL, NULL, 0, NULL} /*Sentinel */
PyMODINIT_FUNC
initpyTEST(void)
PyObject *m;
m = Py_InitModule3("pyTEST", module_methods, "An example demostrates how python c extensions implement and work.");
if (m == NULL) return;
编译:
mingw32-gcc -I./python/include -c pyTEST.c -o pyTEST.o链接:
mingw32-gcc -shared -o pyTEST.pyd pyTEST.o -lpython27 -L./python/libs测试脚本
#coding: utf-8
import pyTEST
print pyTEST.get_version()
执行结果
1.0
这种方式的c扩展有许多细节需要注意,比如引用计数的增减处理(Py_INCREF, Py_DECREF等宏的使用),处理错误就会造成内存泄漏或者非法内存访问,需要对borrowing references和new references有清晰的概念。
咱们来个栗子,做个测试,改一下get_version:
static PyObject *
get_version(PyObject *self, PyObject *args)
PyObject *ret = Py_BuildValue("s", "1.0");
Py_DECREF(ret);
return ret;
再修改测试脚本:
#coding: utf-8
import pyTEST
import gc
v = pyTEST.get_version()
print v
gc.collect()
print "here, %s" % v
你就会发现Python挂了并提示:Fatal Python error:deletion of interned string failed。这其中有许多细节,比如返回的类型不同,结果也不同,大家可以自行测试。
三、SIP,Boost.Python, SWIG, Cython, Pybindgen
sip是Qt移植到python过程中开发的,wxWidget也有大量使用;cython一般多用于科学计算领域的加速。
这方面本人应用用的不多,就不在此误人子弟。期待高手们解答,分享经验。