为Python写c扩展

最近在使用Python编写一些控制程序,由于需要控制一些CCD相机之类的外设,而这些外设通常又只提供c的SDK,因此需要自己编写一些c/c++的扩展程序,方便用python来控制这些设备。下面介绍一下我的编写经验。
  使用工具:VS2017;python 3.6;操作系统:win10,x64。
  首先,安装VS2017:
  社区版的VS2017可以在官网上免费下载和安装使用。当然,在免费使用一个月后会要求你登录账号,免费注册一个账号然后乖乖登录就可以了。如果实在不想登录或电脑没有联网,还有一个办法就是修改电脑的本地日期,不超过安装日期一个月就行。
  VS2017的安装很简单,需要选择安装的模块如下图所示,注意一定要勾选Python开发模块,这是我们主要使用的模块。
为Python写c扩展_第1张图片
  创建Python扩展模块:
  VS2017安装完成后,运行,进入新建项目窗口,如下图所示,选择Python扩展模块,设置好模块的名称和文件位置。这里我设置的模块名称是Extention,位置在D盘Python\c extention文件夹下。设置完成后单击确定即可。

为Python写c扩展_第2张图片
  创建好的Python扩展模块如下图所示。通常需要先在左侧解决方案资源管理器界面右键单击项目名称,选择属性,进入项目属性设置界面。在属性设置界面,需要设置你的编译平台和Windows SDK版本,如下图所示,如果是64位的系统平台就选x64,SDK选择最新的版本。
为Python写c扩展_第3张图片
为Python写c扩展_第4张图片
  属性设置完成后,扩展模块程序应该就可以正确编译了。
  接下来,打开项目Source Files文件夹下的Extenstion.c文件,开始编写扩展程序。
  首先,来看一下我们的扩展程序,
  第一行#include ,需要包含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_NOARGSMETH_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)即可得到预期的结果了。

你可能感兴趣的:(Pyton语言)