Python的轮子众多,实际上其中大部分的轮子为了保证效率,都是使用C/C++实现的。创建Python扩展的步骤如下:
创建扩展代码
创建文件 HelloWorld.c
#include <stdio.h>
int main()
{
puts("Hello, World!");
return 0;
}
使用gcc编译运行看看是否有错误:
$ gcc HelloWorld.c -o hw $ ./hw Hello, World!
使用模板来封装代码
1. 引入Python头文件
这个文件一般在/usr/local/include/python2.x
在HelloWorld.c文件中第一行加入 #include <Python.h>
2. 为模块的每一个函数增加一个型如 static PyObject* Module_func()的包装函数
包装函数的主要作用就是与Python进行交互,一般就是从Python里取数据,将Python类型的数据转化成C类型的数据,然后交给C模块处理数据,然后再将结果转换成Python类型的数据返回。为main函数添加如下包装函数
static PyObject * HelloWorld_main(PyObject * self, PyObject * args) {
main();
return (PyObject*) Py_BuildValue("");
}
因为这个Hello,World函数太简单,不需要传入参数,但是返回值总是需要的,直接返回NULL会产生一个TypeError
,对于我们这里的情形需要返回一个None,即在Python里面表示没有返回值。Py_BuildValue
将C的类型转换为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("ss", "hello", "world") ('hello', 'world') Py_BuildValue("s#", "hello", 4) '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))
3. 在模块中增加一个PyMethodDef类型的静态数组 这个数组的作用是在Python解释器导入的时候使用 格式如下:每一个数组都包含一个函数的信息,最后一个数组放置两个NULL值,代表声明结束
static PyMethodDef methods[] = {
{"main", HelloWorld_main, METH_VARARGS},
{NULL, NULL},
};
METH_VARARGS代表参数以tuple的形式传入。如果我们需要使用PyArg_ParseTupleAndKeywords() 函数来分析关键字参数的话,这个标志常量应该写成: METH_VARARGS & METH_KEYWORDS,进行逻辑与运算。
增加模块初始化函数void initMethod()
最后的工作就是模块的初始化工作。这部分代码在模块被python导入时进行调用。 void initHelloWorld(){ Py_InitModule(“HelloWorld”,methods); }
最终 HelloWorld.c 全部代码如下:
#include <Python.h>
#include <stdio.h>
int main()
{
puts("Hello, Wolrd!");
return 0;
}
static PyObject *
HelloWorld_main(PyObject * self, PyObject * args) {
main();
return (PyObject*) Py_BuildValue("");
}
static PyMethodDef methods[] = {
{"main", HelloWorld_main, METH_VARARGS},
{NULL, NULL},
};
void initHelloWorld(){
Py_InitModule("HelloWorld",methods);
}
编译代码
创建setup.py
我们在安装python第三方包的时候,很多情况下会用到python setup.py install这个命令, 下面我们来了解一下setup.py文件的内容。
编译的最主要的内容由setup函数完成,你需要为每一个扩展创建一个Extension实例,在这里我们只有一个 扩展,所以只需要创建一个实例。 Extension(‘Extest’, sources=[‘Extest.c’]),第一个参数是扩展的名字,如果模块是包的一部分,还需要加”.”; 第二个参数是源代码文件列表 setup(‘Extest’, ext_modules=[…]),第一个参数表示要编译哪个东西,第二个参数列出要编译的Extension对象
#!/usr/bin/python
from distutils.core import setup, Extension
MOD_NAME = "HelloWorld"
setup(name = MOD_NAME, ext_modules = [Extension(MOD_NAME, sources = ["HelloWorld.c"])])
通过运行setup.py来编译和连接你的代码
通过一下命令编译模块,并安装模块
$ python setup.py build $ python setup.py install
然后在Python使用模块
Python 2.7.10 (default, Oct 23 2015, 19:19:21) [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import HelloWorld >>> HelloWorld.main() Hello, Wolrd! >>>
或者不安装进行调试
#!/usr/bin/python
from ctypes import *
import os
#需要使用绝对路径
module = cdll.LoadLibrary(os.getcwd() + '/HelloWorld.so')
module.main()