使用VS2010编写Python的C扩展

        Python语言的特性具有良好的开发效率,使其在诸多领域备受青睐。然而Python语言执行效率的缺陷,使得他在许多对工程效率要求严格的领域的应用受到了限制。但好在Python具有优秀的扩展性能(常常被人称作胶水语言),能通过C\C++、Java等多种语言对其进行扩展。这样便允许我们在工程中使用C语言扩展Python核心代码,同时也能在Python工程中如同调用普通模块一样对C扩展进行调用。使得Python工程在保留其特性的前提下同时拥有C的执行效率,听起来是不是十分美好。接下来简单介绍使用C通过编写动态链接库dll的方法扩展Python。(实例环境:Windows7、Visual Studio 2010、Python 2.7.10)

       如何将C语言与Python语言结合在一起呢?那最重要的就是C扩展的中的三个部分:初始化函数、导出函数和方法列表了。下面我们就来一一介绍。

       在VC2010中新建Win32工程PythonDemo,在向导中选择dll,完成。

       首先在PythonDmeo.cpp中加载头文件:

#include Python.h


  与Python相关的函数和宏都在该头文件中。若在编译时出现无法找到该文件或无法找到Python28_d.lib的错误,表示还未安装Python或未设置环境变量,可以在工程中手动添加。具体方法:在项目——属性中:
1、配置属性——VC++目录——包含目录添加路径:...\Python2.7.10\include
2、配置属性——VC++目录——库目录添加路径:...\Python2.7.1\libs
3、配置属性——链接器——输入:加入Python27_d.lib
其他具体问题参见:http://blog.csdn.net/catalyst_zx/article/details/47131533


一、初始化函数:

      初始化函数的作用是对扩展模块进行初始化,实例代码如下:

//
//函数:扩展模块初始化函数
//功能:对扩展模块进行初始化
//返回:PyMODINT_FUNC
//
PyMODINIT_FUNC initPythonDemo(void)
{
	Py_InitModule("PythonDemo",PyPythonMethod);
}

        

        初始化函数的名称是固定的,必须是init+模块名称。在Python导入模块时,首先会在模块中寻找调用该函数,对扩展模块进行初始化。该函数的返回值为PyMODINT_FUNC,在工程中我们可以看到其定义:

define PyMODINIT_FUNC extern "C" __declspec(dllexport) void

        

        表示该函数返回值相当于C中的void。

        在初始化函数内部,我们调用了Py_InitModule()函数,该函数的作用是将模块名字和方法列表结合起。第一个参数为模块的名称,第二个参数为方法列表。Python通过方法列表确定在调用时调用C扩展中的哪个函数,具体有关方法列表我们将在后面介绍。Py_InitModule()函数返回值为一个指向该模块的指针,当返回值为NULL时表示初始化失败。


二、方法列表

       方法列表是一个C的结构体数组,Python在调用模块函数时,通过它找到模块中具体对应要调用的函数。

//
//方法列表
//
static PyMethodDef PyPythonMethod[]=
{
	{"PyTest",PyTest,METH_VARARGS,"This function is used for testing !"},
	{NULL,NULL,0,NULL}
};

       

        第一个字段为在Python中调用时所使用的方法名称;第二个为该方法的导出函数,即实际调用的函数;第三个表示参数传递的模式,可选的两种方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递;第四个参数为对该函数的说明,在python里来help这个函数,将显示这个说明。

      方法列表始终以{NULL,NULL,0,NULL}结尾。


三、导出函数:

          导出函数也称为包裹函数,是Python与C交互的关键。其作用是:1、将Python传入的参数转换为C\C++格式。2、将转换好的参数传给C扩展中的函数,并接受返回值。3、将返回值转换为Python格式输出。

          例如在C扩展模块中,我们有一个函数:

//
//扩展模块测试函数
//功能:测试扩展模块
//输入:int i
//输出:(int)i+1
//
int PyTest(int i)
{
	return i+1;
}

         

        则可以编写导出函数:

//
//测试函数导出函数
//功能:导出测试函数
//输入:PyObject
//输出:PyObject*
//
static PyObject *PyTest(PyObject *self,PyObject *args)
{
	int i;
	if(!PyArg_ParseTuple(args,"i",&i))
		return NULL;
	int k=PyTest(i);
	return Py_BuildValue("i",k);
}

        在C\C++中,Python中的对象统一都用PyObject类来表示。导出函数有两个参数第一个self一般为空,或为一个初始化的指向模块的指针(见Py_InitModule);第二册参数是一个指向Python传入参数元组\字典的指针。在导出函数中我们使用PyArg_ParseTuple对Python传入的参数元组args进行解析。

        PyArg_ParseTuple的第一个参数为我们要转换的参数;第二个格式转换符号,i表示int,s表示string,不同参数的转化符号纸间用|隔开,入i|i表示两个参数均为int型;第三个参数是转换后参数实际存放的位置。

        在调用测试函数获得返回值k后,我们在返回时通过Py_BuildValue函数将C格式的参数k转换为Python格式返回。Py_BuildValue的第一个参数为格式转换符号,第二个为要转换的变量。

        如果对应的C函数没有返回值(即返回值类型为void),则应返回一个全局的None对象(Py_None),并将其引用计数增,如下所示:

Py_INCREF(Py_None);
returnPy_None;


四、完成并测试

        在release下编译,将得到的dll复制到Python安装目录或工程目录下,并将其扩展名改为.pyd。

        在Python IDLE中进行测试:

>>> import PythonDemo
>>> PythonDemo.PyTest (1)
2
>>>


五、疑难解决

       使用VS2010编写的dll在Python调用总会莫名的出现”无初始化函数“的错误(实际上编写时有初始化函数),百思不得其解,最后历尽千辛万苦终于在网上找到的解决方案http://m.blog.csdn.net/blog/kosl90/6618367

        即在VC2010链接器命令行中需要对初始化函数进行导出,具体方法:配置属性>>连接器>>命令行>>其他选项中添加”/export:模块初始化方法名”。

       至于为何不导出,会在Python导入模块时出现模块为定义初始化方法的错误,具体原因仍不详。


六、参考文献:

       1、官方文档:https://docs.python.org/2/extending/extending.html#a-simple-example

       2、C++编写Python扩展:http://my.oschina.net/u/2306127/blog/369997

       3、Windows中使用VC2010扩展:http://m.blog.csdn.net/blog/kosl90/6618367

       4、C、Python混合编程:http://www.zhihu.com/question/23003213/answer/56121859


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值