美文网首页个人专题工具癖C++
使用C++编写Python扩展

使用C++编写Python扩展

作者: dalalaa | 来源:发表于2018-09-14 16:12 被阅读29次

    Python的很多库中都包含了C/C++的代码,在安装这种库的时候,尝尝会遇到这样的报错:

    error: unable to find vcvarsall.bat

    通常的解决方案有两种:

    1. 安装visual studio或者mingw;
    2. 使用编译好的Python库文件,进行安装。

    一般大家都会选择安装编译过的库文件,或者安装mingw,但是如果要在Windows下为自己的Python程序编写扩展,安装visual studio是最优的解决方案。

    下面介绍使用C++编写Python扩展模块的常见写法。

    传入有限个数据

    先建立一个Exten.cpp文件,编写如下代码:

    #include "Python.h"
    // 函数主体
    int add(int a,int b) {
        return a + b;
    }
    
    // 包裹函数
    static PyObject *Exten_add(PyObject *self,PyObject *args) {
        int a,b;
        // 获取数据,i代表int,ii代表两个int
        // 如果没有获取到,则返回NULL
        if (!PyArg_ParseTuple(args,"ii",&a,&b)) {
            return NULL;
        }
    
        return (PyObject*)Py_BuildValue("i",add(a,b));
    }
    
    // 添加PyMethodDef ModuleMethods[]数组
    static PyMethodDef ExtenMethods[] = {
        // add:可用于Python调用的函数名,Exten_add:C++中对应的函数名
        {"add",Exten_add,METH_VARARGS}, 
        {NULL,NULL},
    };
    
    // 初始化函数
    static struct PyModuleDef ExtenModule = {
        PyModuleDef_HEAD_INIT,
        "Exten",//模块名称
        NULL,
        -1,
        ExtenMethods
    };
    
    void PyInit_Exten() {
        PyModule_Create(&ExtenModule);
    }
    

    再在同文件夹下建立setup.py文件,编写如下代码:

    from distutils.core import setup,Extension
    
    MOD = 'Exten' #模块名
    setup(name=MOD,ext_modules=[Extension(MOD,sources=['D:\\Exten.cpp'])])
    

    然后在CMD控制台输入:

    python D:\\setup.py build
    python D:\\setup.py install
    

    此模块便已经安装成功了。可以直接在Python代码中调用:

    >>> import Exten
    >>> Exten.add(1,3)
    4
    

    传入可迭代对象

    这种方式只能处理单个数据传入的任务,如果需要处理批量数组等批量数据,则需要涉及到Python对象到C++对象的转化。

    以max函数为例,添加max函数之后,还需要为max编写包裹函数,整个Exten文件中代码如下:

    #include "Python.h"
    #include <vector>
    #define INT_MIN -2147483648
    
    using namespace std;
    
    // 函数主体
    int add(int a,int b) {
        return a + b;
    }
    
    int max(vector<int> lst) {
        int max_num = INT_MIN;
        for (int i =0; i< lst.size(); i++) {
            if (lst[i] > max_num) {
                max_num = lst[i];
            }
        }
        return max_num;
    }
    // 包裹函数
    static PyObject *Exten_add(PyObject *self,PyObject *args) {
        int a,b;
        // 如果没有获取到数据
        if (!PyArg_ParseTuple(args,"ii",&a,&b)) {
            return NULL;
        }
    
        return (PyObject*)Py_BuildValue("i",add(a,b));
    }
    
    // 模块中每个可供Python调用的函数都需要一个对应的包裹函数
    static PyObject *Exten_max(PyObject *self,PyObject *args) {
        PyObject *obj;
        vector<int> lst;
        // O代表对象
        if (!PyArg_ParseTuple(args,"O",&obj)) {
            return NULL;
        }
    
        // 获取可迭代对象
        PyObject *iter = PyObject_GetIter(obj);
        if (!iter) {
            PyErr_SetString(PyExc_TypeError,"The object is not iterable!");
            return NULL;
        }
    
        while (true) {
            // 逐个获取列表中的各个元素
            PyObject *next = PyIter_Next(iter);
            if (!next) {
                break;
            }
            // 检查是否为long或者long的子类(包括int)
            if (!PyLong_Check(next)) {
                PyErr_SetString(PyExc_TypeError,"Int or Long list is expected!");
                return NULL;
            }
            // 由Python的Long转化为C/C++的long
            long num = PyLong_AsLong(next);
            lst.push_back(num);
        }
        return (PyObject*)Py_BuildValue("i",max(lst));
    }
    
    // 添加PyMethodDef ModuleMethods[]数组
    static PyMethodDef ExtenMethods[] = {
        // add:可用于Python调用的函数名,Exten_add:C++中对应的函数名
        {"add",Exten_add,METH_VARARGS},
        {"max",Exten_max,METH_VARARGS},
        {NULL,NULL},
    };
    
    // 初始化函数
    static struct PyModuleDef ExtenModule = {
        PyModuleDef_HEAD_INIT,
        "Exten",//模块名称
        NULL,
        -1,
        ExtenMethods
    };
    
    void PyInit_Exten() {
        PyModule_Create(&ExtenModule);
    }
    

    采取同样的方式build + install之后,就可以使用了:

    >>> import Exten
    >>> Exten.max([1,2,3,4])
    4
    >>> Exten.max([1,2,3,4.1])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Int or Long list is expected!
    

    参考:https://blog.csdn.net/baidu_35085676/article/details/79518777

    https://stackoverflow.com/questions/22458298/extending-python-with-c-pass-a-list-to-pyarg-parsetuple

    https://docs.python.org/3/c-api/long.html

    相关文章

      网友评论

        本文标题:使用C++编写Python扩展

        本文链接:https://www.haomeiwen.com/subject/qwiogftx.html