【Python】C++ & Python 混合编程(2)-- Python 调用 C++

  • 比C/C++调用Python更有意义:先用 Python 快速开发出大部分功能,需要性能优化的部分再优化到C/C++
  • 基本思路是,将C/C++封装为动态链接库(windows下为 .pyd,linux下为 .so)供 Python 调用
  • 将分4篇介绍Python扩展的方法、Cython方法SWIG方法ctypes方法

Python 扩展方法

封装若干函数到动态链接库

1)代码

#include "stdafx.h"
#include "iostream"
#include "Python.h"
using namespace std;

int add_it(int a, int b){
    int c = a+b;
    cout << "【C/C++】" << a << " + " << b << " = "<<c<<endl;
    return c;
}

int sub_it(int a, int b){
    int c = a-b;
    cout << "【C/C++】" << a << " - " << b << " = "<<c<<endl;
    return c;
}

// 包裹函数,实现 Python 参数类型到 C/C++ 参数类型的转换
static PyObject* _add_it(PyObject* self, PyObject* args){
    int _a, _b;
    int res;

    if(!PyArg_ParseTuple(args, "ii", &_a, &_b))
        return 0;
    res = add_it(_a, _b);
    return PyLong_FromLong(res);
}

static PyObject* _sub_it(PyObject* self, PyObject* args){
    int _a, _b;
    int res;

    if(!PyArg_ParseTuple(args, "ii", &_a, &_b))
        return 0;
    res = sub_it(_a, _b);
    return PyLong_FromLong(res);
}

// 负责告诉 Python 这个模块里有哪些函数可以被调用
// 每一项有 4 个参数
static PyMethodDef ModuleMethods[] = {
    {
        "add_it",     //  Python 环境下的函数名称
        _add_it,      //  包裹函数
        METH_VARARGS, // 参数形式,将所有参数以元组方式打包
        ""            // 说明
    },
    {
        "sub_it",
        _sub_it,
        METH_VARARGS,
        ""
    },
    {NULL, NULL, 0, NULL}
};

// 导出函数将模块名称与导出表连接
// c_module 是导出链接库的名字,对应导出函数名 initXXX
PyMODINIT_FUNC initc_module(void){
    (void) Py_InitModule("c_module", ModuleMethods);
}

2)编译环境配置

  • Windows
    以VS2010为例,添加 Python 的 include 和 lib
          这里写图片描述

  • Ubuntu
    有2点需要注意:1. 默认导出名字会在前面加 lib ,所以要修改 initc_module 函数名字
            2.initc_module 前加 extern “C”

extern "C" PyMODINIT_FUNC initlibc_module(void){
    (void) Py_InitModule("libc_module", ModuleMethods);
}

.pro 文件

QT       += core

QT       -= gui

TARGET = c_module

TEMPLATE = lib

INCLUDEPATH += /usr/include/python2.7\
                              /usr/include/x86_64-linux-gnu/python2.7\
                             -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes
LIBS += -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7\
        -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

SOURCES += main.cpp

3)Python 端直接 import 模块
                  这里写图片描述

封装类到动态链接库

重点参考文章《C++导出类到Python》

从底层到顶层包括以下部分

  • 类(以结构方式)数据成员定义及说明
  • 类所有方法(包括构造和析构)的定义及说明
  • 类的说明
  • 模块的说明
  • 模块的初始化函数(import时自动调用)

以下代码摘自文章《C++导出类到Python》
实现一个 CScore 类,记录名字、数学、英语成绩

#include <Python.h>
#include <structmember.h>   // Python/include/structmember.h

1)数据成员定义及声明

//////////////////////////////////////////////////////////////////////////
// 类/结构的定义.
//
typedef struct _CScore
{
    PyObject_HEAD             // == PyObject ob_base;  定义一个PyObject对象.
    //////////////////////////////////////////////////////////////////////////
    // 类/结构的真正成员部分.
    //
    char* m_szName;
    float m_dMath;
    float m_dEnglish;
}CScore;


static PyMemberDef CScore_DataMembers[] =        //类/结构的数据成员的说明.
{
    {"m_szName",   T_STRING, offsetof(CScore, m_szName),   0, "The Name of instance"},
    {"m_dMath",    T_FLOAT,  offsetof(CScore, m_dMath),    0, "The Math score of instance."},
    {"m_dEnglish", T_FLOAT,  offsetof(CScore, m_dEnglish), 0, "The English score of instance."},
    {NULL, NULL, NULL, 0, NULL}
};

2)类所有方法定义

//////////////////////////////////////////////////////////////////////////
// CScore类的所有内置、构造方法.
//
static void CScore_init(CScore* Self, PyObject* pArgs)    //构造方法.
{
    const char* Name = 0;
    if(!PyArg_ParseTuple(pArgs, "sff", &Name, &Self->m_dMath, &Self->m_dEnglish))
    {
        cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
        return ;
    }
    Self->m_szName = new char[strlen(Name) + 1];
    strcpy(Self->m_szName, Name);
}

static void CScore_Destruct(CScore* Self)
{
    if(Self->m_szName)
        delete [] Self->m_szName;                  //先释放其字符指针对象.

    //如果还有PyObject*成员的话,要一并释放之.
    //如:Py_XDECREF(Self->Member);
    Py_TYPE(Self)->tp_free((PyObject*)Self);      //释放对象/实例.
}

static PyObject* CScore_Str(CScore* Self)         //调用str/print时自动调用此函数.
{
    ostringstream OStr;
    OStr<<"Name    : "<<Self->m_szName<<endl
        <<"Math    : "<<Self->m_dMath<<endl
        <<"English : "<<Self->m_dEnglish<<endl;
    string Str = OStr.str();
    return Py_BuildValue("s", Str.c_str());
}

// repr(object) 将python对象转为字符串形式
static PyObject* CScore_Repr(CScore* Self)        //调用repr内置函数时自动调用.
{
    return CScore_Str(Self);
}


//////////////////////////////////////////////////////////////////////////
// CScore类的所有Get方法.
//
static PyObject* CScore_GetName(CScore* Self)
{
    return Py_BuildValue("s", Self->m_szName);
}

static PyObject* CScore_GetMath(CScore* Self)
{
    return Py_BuildValue("f", Self->m_dMath);
}

static PyObject* CScore_GetEnglish(CScore* Self)
{
    return Py_BuildValue("f", Self->m_dEnglish);
}


//////////////////////////////////////////////////////////////////////////
// CScore类的所有Set方法.
//
static PyObject* CScore_SetMath(CScore* Self, PyObject* Argvs)
{
    Py_INCREF(Py_None);
    if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dMath))
    {
        cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
        return Py_None;
    }

    return Py_None;
}

static PyObject* CScore_SetEnglish(CScore* Self, PyObject* Argvs)
{
    Py_INCREF(Py_None);
    if(!PyArg_ParseTuple(Argvs, "f", &Self->m_dEnglish))
    {
        cout<<"Parse the argument FAILED! You should pass correct values!"<<endl;
        return Py_None;
    }

    return Py_None;
}

static PyObject* CScore_PrintInfo(CScore* Self)     //输出平均分.
{
    cout<<"The scores as follows:"<<endl
        <<"=============================="<<endl
        <<"Name    : "<<Self->m_szName<<endl
        <<"Math    : "<<Self->m_dMath<<endl
        <<"English : "<<Self->m_dEnglish<<endl
        <<"=============================="<<endl;

    Py_XINCREF(Py_None);
    return Py_None;
}

3)类方法的列表说明

static PyMethodDef CScore_MethodMembers[] =         //类的所有成员函数结构列表.
{
    {"GetName",    (PyCFunction)CScore_GetName,    METH_NOARGS,  "Get the name of instance."},
    {"GetMath",    (PyCFunction)CScore_GetMath,    METH_NOARGS,  "Get the math score of instance."},
    {"GetEnglish", (PyCFunction)CScore_GetEnglish, METH_NOARGS,  "Get the english score of isntance."},

    {"SetMath",    (PyCFunction)CScore_SetMath,    METH_VARARGS, "Set the math score of instance."},
    {"SetEnglish", (PyCFunction)CScore_SetEnglish, METH_VARARGS, "Set the english of instance."},

    {"PrintInfo",  (PyCFunction)CScore_PrintInfo,  METH_NOARGS,  "Print all information of instance."},
    {NULL, NULL, NULL, NULL}
};

4)类数据及方法的说明

//////////////////////////////////////////////////////////////////////////
// 类/结构的所有成员、内置属性的说明信息.
//
static PyTypeObject CScore_ClassInfo =
{
    PyVarObject_HEAD_INIT(NULL, 0)"Module.MyCppClass",  //可以通过__class__获得这个字符串. CPP可以用类.__name__获取.
    sizeof(CScore),                 //类/结构的长度.调用PyObject_New时需要知道其大小.
    0,
    (destructor)CScore_Destruct,    //类的析构函数.
    0,
    0,
    0,
    0,
    (reprfunc)CScore_Repr,
    0,
    0,
    0,
    0,
    0,
    (reprfunc)CScore_Str,         //Str/print内置函数调用.
    0,
    0,
    0,
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,     //如果没有提供方法的话,为Py_TPFLAGS_DEFAULE
    "MyCppClass Objects---Extensioned by C++!",   //__doc__,类/结构的DocString.
    0,
    0,
    0,
    0,
    0,
    0,
    CScore_MethodMembers,       //类的所有方法集合.
    CScore_DataMembers,         //类的所有数据成员集合.
    0,
    0,
    0,
    0,
    0,
    0,
    (initproc)CScore_init,      //类的构造函数.
    0,
};

5)模块的说明及初始化
这里 Python3.X 和 Python2.X 有差异
模块名称为 Score 里面包含类 CScore

// for python3.x
//////////////////////////////////////////////////////////////////////////
// 此模块的说明信息.
// python2.x 里没有 PyModuleDef 及 PyModuleDef_HEAD_INIT 的定义

static PyModuleDef ModuleInfo =
{
    PyModuleDef_HEAD_INIT,
    "My C++ Class Module",               //模块的内置名--__name__.
    "This Module Created By C++--extension a class to Python!",  //模块的DocString.__doc__
    -1,
    NULL, NULL, NULL, NULL, NULL
};


//////////////////////////////////////////////////////////////////////////
// 模块的初始化函数. import 时自动调用.
//
PyMODINIT_FUNC           // == __decslpec(dllexport) PyObject*, 定义导出函数.
PyInit_Score(void)       //模块外部名称为--CppClass
{
    PyObject* pReturn = 0;
    CScore_ClassInfo.tp_new = PyType_GenericNew;   //此类的new内置函数—建立对象.

    //////////////////////////////////////////////////////////////////////////
    // 完成对象类型的初始化—包括添加其继承特性等等。
    // 如果成功,则返回0,否则返回-1并抛出异常.
    //
    if(PyType_Ready(&CScore_ClassInfo) < 0)
        return NULL;

    pReturn = PyModule_Create(&ModuleInfo);         //根据模块信息创建模块.
    if(pReturn == 0)
        return NULL;

    Py_INCREF(&ModuleInfo);
    PyModule_AddObject(pReturn, "CScore", (PyObject*)&CScore_ClassInfo); //将这个类加入到模块的Dictionary中.
    return pReturn;
}
// for python2.x
PyMODINIT_FUNC           // == __decslpec(dllexport) PyObject*, 定义导出函数.
initScore(void)       //模块外部名称为--CppClass
{
    PyObject* pReturn = 0;
    CScore_ClassInfo.tp_new = PyType_GenericNew;   //此类的new内置函数—建立对象.

    //////////////////////////////////////////////////////////////////////////
    // 完成对象类型的初始化—包括添加其继承特性等等。
    //
    if(PyType_Ready(&CScore_ClassInfo) < 0)
        return;
    //根据模块信息创建模块.
    pReturn = Py_InitModule("Score", CScore_MethodMembers);
    if(pReturn == 0)
        return;

    PyModule_AddObject(pReturn, "CScore", (PyObject*)&CScore_ClassInfo); //将这个类加入到模块的Dictionary中.
    return;
}

                这里写图片描述

其他
  • 编译环境与上一节一样
  • 有一个比较麻烦的“引用计数”的问题(Py_INCREF() 和 Py_DECREF()),处理不当会引起内存泄露
    参考文章《Python引用计数(Reference Count)》,留个坑以后慢慢填

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值