1. 直接C扩展Python的方法
Python的扩展API是主要为C语言提供的,可以利用该API为要导出的C函数建立包装器(wrapper)。包装器用来处理Python对象与底层C函数中的变量所需值之间的转换,并将C函数注册到Python的自定义module中。
假设将要导出的函数成为模块函数,对应wrapper中的函数为包装函数。则wrapper文件主要包含以下几个部分:
include "Python.h";
每个模块函数的包装函数,即:PyObject* Module_func();
模块函数与包装函数的映射表,即:PyMethodDef ModuleMethods[]对应表;
模块的初始化函数:void initModule()部分。
简单示例程序:
假设有以下C文件TestModel.c:
int add(int x,int y)
{
return x+y;
}
我们要导出其中的函数add(),则要为之写wrapper。文件内容如下:
include "Python.h";
extern int add(int);
PyObject *add_wrap(PyObject *self, PyObject *args)
{
int x=0 ;
int y=0;
int result=0;
if (! PyArg_ParseTuple(args, "i|i", &x, &y))
return NULL;
result = add(x,y)
return Py_BuildValue("i", result);
}
参数说明:
第一个参数是self,这个是python用的,每个函数都要有。我们暂时不管。
第二个参数是args,它是一个参数列表。她把所有的参数都整合成一个string。所以我们需要从这个string里来解析我们的参数。我们使用python内置函数PyArg_ParseTuple来完成这个任务。其中,第一个参数是args,就是我们要转换的参数;第二个是格式符号。“s”代表是个string。从args里提取一个参数就写"s", 两个的话就写"s|s", 如果是一个string,一个int,就写"s|i", 和printf差不多;第三个参数就是提取出来的参数放置的真正位置。必须传递这个参数的地址。对于add,他将提取两个参数。分别是x和y。
调用完add后,我们需要返回结果。这个结果是c的type或者是我们自己定义的类型。必须把它转换成PyObject,让python认识。这个用Py_BuildValue来完成。它是PyArg_ParseTuple的逆过程。他的第一个参数和PyArg_ParseTuple的第二个参数一样,是个格式化符号。第二个参数是我们需要转换的参数。Py_BuildValue会把所有的返回值组装成一个tutple给python。
static PyMethodDef TestModelMethods[] =
{
{"add", add_wrap, METH_VARARGS, "add two number to one number"},
{NULL, NULL, 0, NULL}
};
这个是一个c的结构。它来完成一个映射。我们需要把我们扩展的函数都映射到这个表里。表结构说明如下:
第一个字段是python真正认识的。是python里的方法名字。
第二个字段是python里的这个方法名字的具体实现的函数名。在python里调用add,真正执行的是用c写的add_wrap函数。
第三个字段是METH_VARARGS,他告诉python,add_wrap是调用c函数来实现的。
第四个字段是这个函数的说明。如果你在python里来help这个函数,将显示这个说明。相当于在python里的函数的文档说明。
void initTestModel(void)
{
Py_InitModule("TestModel", TestModelMethods);
}
注意,这个函数的名字不能改动。必须是init+模块名字。我们的模块名字是TestModel。所以这个函数是initTestModel()。
这样python在导入TestModel的模块时候,才会找到initTestModel()这个函数,并调用。这个函数调用Py_InitModule来将模块名字和映射表结合在一起。
他表示,TestModel这个模块使用TestModelMethods这个映射表。python应该这样导入我们的module的.然后我们可以将生成的模块导入到Python环境中,并调用其中的函数。
2. 怎样用SWIG生成C的wraper
SWIG是一个自动的扩展构造工具。它读入注释的源程序头文件(后缀为.i的脚本文件),为python、tcl、perl等多种脚本语言产生wrap代码。
步骤大致为:
(1) 写源程序;
(2) 写后缀为.i的脚本文件;
(3) 使用命令"swig -python TestModel.i"生成TestModel_wrap.c,example_wrap.doc;
(4) 编译连接成共享库。
.i脚本文件的格式很简单,只需要列出要导出的函数或类型,并注释必要信息。还以上面的TestModel.c为例,写TestModel.i脚本文件如下:
%module TestModel
%{
#include "TestModel.h"
%}
extern int add(int);
在命令行输入以下命令"swig -python TestModel.i",会在当前目录下生成TestModel_wrap.c,TestModel_wrap.doc两个文件。前者即为TestModel.c的wrap文件,后者为解释文件。
接下来使用编译连接命令生成TestModel.so文件。命令同第一节:
接下来就可以在Python环境中使用TestModel模块中的函数了。
3. 用SWIG生成C++的wraper
用SWIG生成C++的wrapper过程类似于C wrapper。它的步骤如下:
(1) 写.cpp源程序;
(2) 写.i脚本文件;
(3) swig -c++ -python foo.i
生成TestModel_wrap.c,TestModel_wrap.doc;
(4) 编译连接成共享库TestModel.so。
则生成Python的foo模块,可以通过调入foo模块使用其中的类成员函数、成员变量。
一个简单示例:
假设有如下C++源程序:
class TestModel
{
public:
TestModel();
void add(int value);
};
对应实现的cpp文件如下:
#include "TestModel.h"
TestModel::TestModel()
{
printf("测试模块");
}
int TestModel::add(int x,int y)
{
return x+y;
}
为之写TestModel.i文件:
%module TestModel
%{
#include "TestModel.h"
%}
class TestModel
{
public:
TestModel();
int add(int x,int y);
};
编译方式:
(1.控制台方式)
在控制台里执行使用命令:"swig -c++ -python TestModel.i",得到TestModel_wrap.c,TestModel_wrap.doc(有些系统生成的是number.py)两个文件。
生成TestModel.so文件,可以在Python环境中使用TestModel类及其成员函数。但类名和成员函数名稍有变化,具体可见生成的TestModel_wrap.doc(或number.py)文件。
(2.编辑器方式)
在.net编辑器里选中.i文件,然后在属性里进行设置:
命令行:
..\SDK\swigwin\swig -c++ -python -outdir $(ConfigurationName) -I../.. "$(InputPath)"
输出:$(InputDir)$(InputName)_wrap.cxx
并且在工程属性里要指定python库的相关文件,包括include和libs。
最后,进行编译得到*.cxx文件,然后将该文件添加到工程重新进行编译。
也可以在C++里执行,如下:
#include <iostream>
#include <string>
#include "TestModel_wrap.cxx"
int main() {
Py_Initialize(); // initialize python
init_Test(); //在test_wrap.cxx里定义的。
PyRun_SimpleString("import testpy\n
a = TestModel.TestModel()\n
b =a.add(1,2)\n
print b\n");
Py_Finalize(); // shut down python
return 0;
}