专栏首页我的机器学习之路使用C++编写Python3扩展

使用C++编写Python3扩展

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自定义PyTorch中的Sampler

    在训练GAN的过程中,一次只训练一个类别据说有助于模型收敛,但是PyTorch里面没有预设这种数据加载方式,要这样训练的话,需要自己定义Sampler,即自定义...

    带萝卜
  • Kaggle共享单车数据分析——数据可视化

    本文数据来源于Kaggle_Bike_Sharing_Demand。主要内容为模型前期的简要数据分析及可视化。

    带萝卜
  • 百万级类别的分类模型的拆分训练

    很多人脸识别算法都是以分类的方式进行训练的,分类的训练方式中存在一个很大的问题,就是模型的最后一个全连接层的参数量太大了,以512为特征为例:

    带萝卜
  • js 把对象转为json字符串

    function obj2str(o){ var r = []; if(typeof o =="string") return "\""+o.r...

    joshua317
  • 数学--数论--HDU 2674 沙雕题

    WhereIsHeroFrom: Zty, what are you doing ? Zty: I want to calculate N!.. Where...

    风骨散人Chiam
  • 无需自定义View,彻底解放shape,selector吧

    作为一个android程序员,对于shape、selector这两个标签一定不陌生。每当UI设计师给我们设计出一个个button背景的时候,我们就需要去draw...

    我就是马云飞
  • 第二章:Shiro入门——深入浅出学Shiro细粒度权限开发框架

    Shiro是什么   Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能  Shiro能做什么   认证:验证用户...

    MonroeCode
  • SwiftUI:创建隐式动画

    我们回到另一个技术项目,这次我们将要看的是快速,精美且真正被低估的东西:动画 Animation。

    韦弦zhy
  • 洛谷P1081 开车旅行(倍增)

    \(da[i][j]\)同理表示\(a\)的距离,\(db[i][j]\)与\(da\)同理

    attack
  • DevOps 实施中的10个“深坑”,你一定要警惕

    我叫施景丰,来自高效运维社区,多年一线开发、测试、架构经验,在软件方面有十多年的工作经验,聚焦金融行业\通信行业\工业互联网行业\大数据行业工程效率的提升及 D...

    DevOps时代

扫码关注云+社区

领取腾讯云代金券