为Python写c扩展

  • Post author:
  • Post category:python


最近在使用Python编写一些控制程序,由于需要控制一些CCD相机之类的外设,而这些外设通常又只提供c的SDK,因此需要自己编写一些c/c++的扩展程序,方便用python来控制这些设备。下面介绍一下我的编写经验。

使用工具:VS2017;python 3.6;操作系统:win10,x64。

首先,安装VS2017:

社区版的VS2017可以在官网上免费下载和安装使用。当然,在免费使用一个月后会要求你登录账号,免费注册一个账号然后乖乖登录就可以了。如果实在不想登录或电脑没有联网,还有一个办法就是修改电脑的本地日期,不超过安装日期一个月就行。

VS2017的安装很简单,需要选择安装的模块如下图所示,注意一定要勾选Python开发模块,这是我们主要使用的模块。

在这里插入图片描述

创建Python扩展模块:

VS2017安装完成后,运行,进入新建项目窗口,如下图所示,选择Python扩展模块,设置好模块的名称和文件位置。这里我设置的模块名称是Extention,位置在D盘Python\c extention文件夹下。设置完成后单击确定即可。

在这里插入图片描述

创建好的Python扩展模块如下图所示。通常需要先在左侧解决方案资源管理器界面右键单击项目名称,选择属性,进入项目属性设置界面。在属性设置界面,需要设置你的编译平台和Windows SDK版本,如下图所示,如果是64位的系统平台就选x64,SDK选择最新的版本。

在这里插入图片描述

在这里插入图片描述

属性设置完成后,扩展模块程序应该就可以正确编译了。

接下来,打开项目Source Files文件夹下的Extenstion.c文件,开始编写扩展程序。

首先,来看一下我们的扩展程序,

第一行

#include <Python.h>

,需要包含Python.h头文件。如果你的python安装后没有添加系统路径,VS2017可能找不到这个头文件而报错,可以手动把该文件的路径加上去,这个文件位于…\Python36\include文件夹下。

接下来,是

PyDoc_STRVAR

函数,这个里面可以写一些本扩展模块的说明和备注,可以先不管它。

再往下是一个

PyObject *Extention_example()

函数。我们需要在这里添加我们的程序,等下我们来详细介绍这个函数。

再往下是

static PyMethodDef Extention_functions[] =...

函数,这里是我们自己定义的函数的一个列表。我们自己写的所有python可调用的函数都要在这里进行登记。

再接下来,就是

int exec_Extention()

函数,点开这个函数,可以发现里面是一些模块作者、版本、创建时间之类的信息,我们也先不用管这个函数。

后面的几个函数我们也都不用管,放在那里就行了。

总得来说,我们需要做的就是编写自己的

PyObject *Extention_yourFunction()

函数,然后在

static PyMethodDef Extention_functions[] =...

里面登记好就可以了。所以最主要的工作还是

PyObject *Extention_yourFunction()

函数的编写。

关于函数

PyObject *Extention_example(PyObject *self, PyObject *args, PyObject *kwargs)

,需要说明的是,Python和c模块之间的数据传递都是通过PyObject来实现的。即Python中所有类型的数据在c看来都是一个PyObject,而c要把自己产生的数据传递给Python也需要先把它包装成一个PyObject才行。因此,我们可以看到

PyObject *Extention_yourFunction()

函数的输入参数和返回值都是PyObject类型的。

我们先来看下面的一个例子:

PyObject *Extention_add(PyObject *self, PyObject *args) {
    /* Shared references that do not need Py_DECREF before returning. */
    PyObject *obj = NULL;
    int a,b, result;

    /* Parse positional and keyword arguments */
    //static char* keywords[] = { "obj", "number", NULL };
    if (!PyArg_ParseTuple(args, "ii", &a,&b)) {
        return NULL;
    }

    /* Function implementation starts here */
	result = a+b;
	return Py_BuildValue("i",result);
}

函数很简单,在python中调用本函数后返回两个数之和。这里面主要用到

PyArg_ParseTuple()



Py_BuildValue()

两个函数。

其中

PyArg_ParseTuple()

函数用于从输入变量args中取出数据并赋给本地变量,其使用方式与print函数类似:int PyArg_ParseTuple(PyObject *arg, char *format, …)。在上面的例子中,

PyArg_ParseTuple(args, "ii", &a,&b)

意思是,从args中取出两个int类型的数并分别赋给a和b,并返回操作的结果,操作成功则返回1,否则0。不同数据类型的格式字符可以参考

链接

函数

Py_BuildValue()

用于将c中的数据打包成PyObject类型,其定义为:

PyObject *Py_BuildValue(char *format, ...)

。本例中,

Py_BuildValue("i",result);

代码的意思是将int类型的数据result打包成PyObject。

函数定义好后,需要在

static PyMethodDef Extention_functions[] =...

中登记才能调用。登记方式如下所示,第一项为在python中调用该函数时使用的函数名;第二项为本文件中定义该函数时使用的函数名;第三项可以选择

METH_NOARGS



METH_VARARGS

,前者表示无参数,后者表示有参数;第四项是该函数的描述,在python中通过help函数可以看到该信息。

static PyMethodDef Extention_functions[] = {
	{ "add", (PyCFunction)Extention_add, METH_VARARGS, "Return an array." },
    { NULL, NULL, 0, NULL } /* marks end of array */
};

至此,就完成扩展函数的编写啦。保存后,右键单击项目名称,选择生成,就会生成一个“Extention.pyd”文件,然后将该pyd文件放在…\Python36\DLLs目录下即可。然后在python中像使用其它模块一样导入我们的扩展模块

import Extention as ex

,然后,调用函数

ex.add(1,2)

即可得到预期的结果了。



版权声明:本文为weixin_43008870原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。