Python调用C++程序的方法详解


Posted in Python onJanuary 24, 2017

前言

大家都知道Python的优点是开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++代码,或是在C++项目中用Python实现外围功能,都可能遇到Python调用C++模块的需求,下面列举出集中c++代码导出成Python接口的几种基本方法,一起来学习学习吧。

原生态导出

Python解释器就是用C实现,因此只要我们的C++的数据结构能让Python认识,理论上就是可以被直接调用的。我们实现test1.cpp如下

#include <Python.h>

int Add(int x, int y)
{
 return x + y;
}

int Del(int x, int y)
{
 return x - y;
}

PyObject* WrappAdd(PyObject* self, PyObject* args)
{
 int x, y;
 if (!PyArg_ParseTuple(args, "ii", &x, &y))
 {
  return NULL;
 }
 return Py_BuildValue("i", Add(x, y));
}

PyObject* WrappDel(PyObject* self, PyObject* args)
{
 int x, y;
 if (!PyArg_ParseTuple(args, "ii", &x, &y))
 {
  return NULL;
 }
 return Py_BuildValue("i", Del(x, y));
}
static PyMethodDef test_methods[] = {
 {"Add", WrappAdd, METH_VARARGS, "something"},
 {"Del", WrappDel, METH_VARARGS, "something"},
 {NULL, NULL}
};

extern "C"
void inittest1()
{
 Py_InitModule("test1", test_methods);
}

编译命令如下

g++ -fPIC -shared test1.cpp -I/usr/include/python2.6 -o test1.so

运行Python解释器,测试如下

>>> import test1
>>> test1.Add(1,2)
3

这里要注意一下几点

  1. 如果生成的动态库名字为test1,则源文件里必须有inittest1这个函数,且Py_InitModule的第一个参数必须是“test1”,否则Python导入模块会失败
  2. 如果是cpp源文件,inittest1函数必须用extern "C"修饰,如果是c源文件,则不需要。原因是Python解释器在导入库时会寻找initxxx这样的函数,而C和C++对函数符号的编码方式不同,C++在对函数符号进行编码时会考虑函数长度和参数类型,具体可以通过nm test1.so查看函数符号,c++filt工具可通过符号反解出函数原型

通过boost实现

我们使用和上面同样的例子,实现test2.cpp如下

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
using namespace boost::python;

int Add(const int x, const int y)
{
 return x + y;
}

int Del(const int x, const int y)
{
 return x - y;
}

BOOST_PYTHON_MODULE(test2)
{
 def("Add", Add);
 def("Del", Del);
}

其中BOOST_PYTHON_MODULE的参数为要导出的模块名字

编译命令如下

g++ test2.cpp -fPIC -shared -o test2.so -I/usr/include/python2.6 -I/usr/local/include -L/usr/local/lib -lboost_python

注意: 编译时需要指定boost头文件和库的路径,我这里分别是/usr/local/include和/usr/local/lib

或者通过setup.py导出模块

#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension

setup(name="PackageName",
 ext_modules=[
  Extension("test2", ["test2.cpp"],
  libraries = ["boost_python"])
 ])

Extension的第一个参数为模块名,第二个参数为文件名

执行如下命令

python setup.py build

这时会生成build目录,找到里面的test2.so,并进入同一级目录,验证如下

>>> import test2
>>> test2.Add(1,2)
3
>>> test2.Del(1,2)
-1

导出类

test3.cpp实现如下

#include <boost/python.hpp>
using namespace boost::python;

class Test
{
public:
 int Add(const int x, const int y)
 {
  return x + y;
 }

 int Del(const int x, const int y)
 {
  return x - y;
 }
};

BOOST_PYTHON_MODULE(test3)
{
 class_<Test>("Test")
  .def("Add", &Test::Add)
  .def("Del", &Test::Del);
}

注意:BOOST_PYTHON_MODULE里的.def使用方法有点类似Python的语法,等同于

class_<Test>("Test").def("Add", &Test::Add);
class_<Test>("Test").def("Del", &Test::Del);

编译命令如下

g++ test3.cpp -fPIC -shared -o test3.so -I/usr/include/python2.6 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下

>>> import test3
>>> test = test3.Test()
>>> test.Add(1,2)
3
>>> test.Del(1,2)
-1

导出变参函数

test4.cpp实现如下

#include <boost/python.hpp>
using namespace boost::python;

class Test
{
public:
 int Add(const int x, const int y, const int z = 100)
 {
  return x + y + z;
 }
};

int Del(const int x, const int y, const int z = 100)
{
 return x - y - z;
}

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(Add_member_overloads, Add, 2, 3)
BOOST_PYTHON_FUNCTION_OVERLOADS(Del_overloads, Del, 2, 3)

BOOST_PYTHON_MODULE(test4)
{
 class_<Test>("Test")
  .def("Add", &Test::Add, Add_member_overloads(args("x", "y", "z"), "something"));
 def("Del", Del, Del_overloads(args("x", "y", "z"), "something"));
}

这里Add和Del函数均采用了默认参数,Del为普通函数,Add为类成员函数,这里分别调用了不同的宏,宏的最后两个参数分别代表函数的最少参数个数和最多参数个数

编译命令如下

g++ test4.cpp -fPIC -shared -o test4.so -I/usr/include/python2.6 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下

>>> import test4
>>> test = test4.Test()
>>> print test.Add(1,2)
103
>>> print test.Add(1,2,z=3)
6
>>> print test4.Del(1,2)
-1
>>> print test4.Del(1,2,z=3)
-1

导出带Python对象的接口

既然是导出为Python接口,调用者难免会使用Python特有的数据结构,比如tuple,list,dict,由于原生态方法太麻烦,这里只记录boost的使用方法,假设要实现如下的Python函数功能

def Square(list_a)
{
 return [x * x for x in list_a]
}

即对传入的list每个元素计算平方,返回list类型的结果

代码如下

#include <boost/python.hpp>

boost::python::list Square(boost::python::list& data)
{
 boost::python::list ret;
 for (int i = 0; i < len(data); ++i)
 {
  ret.append(data[i] * data[i]);
 }

 return ret;
}

BOOST_PYTHON_MODULE(test5)
{
 def("Square", Square);
}

编译命令如下

g++ test5.cpp -fPIC -shared -o test5.so -I/usr/include/python2.6 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下

>>> import test5
>>> test5.Square([1,2,3])
[1, 4, 9]

boost实现了boost::python::tuple, boost::python::list, boost::python::dict这几个数据类型,使用方法基本和Python保持一致,具体方法可以查看boost头文件里的boost/python/tuple.hpp及其它对应文件

另外比较常用的一个函数是boost::python::make_tuple() ,使用方法如下

boost::python::tuple(int a, int b, int c)
{
 return boost::python::make_tuple(a, b, c);
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
rhythmbox中文名乱码问题解决方法
Sep 06 Python
Python下rrdtool模块的基本使用方法
Nov 13 Python
编写Python小程序来统计测试脚本的关键字
Mar 12 Python
Python中进程和线程的区别详解
Oct 29 Python
Python全局变量与局部变量区别及用法分析
Sep 03 Python
pycharm执行python时,填写参数的方法
Oct 29 Python
使用python绘制二元函数图像的实例
Feb 12 Python
Python基于类路径字符串获取静态属性
Mar 12 Python
通过python调用adb命令对App进行性能测试方式
Apr 23 Python
详解使用python爬取抖音app视频(appium可以操控手机)
Jan 26 Python
利用Python读取微信朋友圈的多种方法总结
Aug 23 Python
python双向链表实例详解
May 25 Python
python中import学习备忘笔记
Jan 24 #Python
用python实现简单EXCEL数据统计的实例
Jan 24 #Python
Python如何import文件夹下的文件(实现方法)
Jan 24 #Python
利用Python脚本实现ping百度和google的方法
Jan 24 #Python
解决python2.7用pip安装包时出现错误的问题
Jan 23 #Python
浅谈终端直接执行py文件,不需要python命令
Jan 23 #Python
在Linux命令行终端中使用python的简单方法(推荐)
Jan 23 #Python
You might like
php合并数组array_merge函数运算符加号与的区别
2008/10/31 PHP
PHP保存带BOM文件的方法
2015/02/12 PHP
WordPress中调试缩略图的相关PHP函数使用解析
2016/01/07 PHP
PHP正则验证字符串是否为数字的两种方法并附常用正则
2019/02/27 PHP
基于jquery实现的鼠标滑过按钮改变背景图片
2011/07/15 Javascript
Javascript的getYear、getFullYear、getUTCFullYear异同分享
2011/11/30 Javascript
使用javascipt---实现二分查找法
2013/04/10 Javascript
js浮点数保留两位小数点示例代码(四舍五入)
2013/12/26 Javascript
jquery live()调用不存在的解决方法
2014/02/26 Javascript
javascript中的正则表达式使用指南
2015/03/01 Javascript
javascript深拷贝(deepClone)详解
2016/08/24 Javascript
javascript学习笔记_浅谈基础语法,类型,变量
2016/09/19 Javascript
jquery配合.NET实现点击指定绑定数据并且能够一键下载
2016/10/28 Javascript
AngularJS 在同一个界面启动多个ng-app应用模块详解
2016/12/20 Javascript
Node.js学习之查询字符串解析querystring详解
2017/09/28 Javascript
vue模仿网易云音乐的单页面应用
2019/04/24 Javascript
Using Django with GAE Python 后台抓取多个网站的页面全文
2016/02/17 Python
Python pass详细介绍及实例代码
2016/11/24 Python
详解python使用递归、尾递归、循环三种方式实现斐波那契数列
2018/01/16 Python
Numpy截取指定范围内的数据方法
2018/11/14 Python
python之PyQt按钮右键菜单功能的实现代码
2019/08/17 Python
Django框架 querySet功能解析
2019/09/04 Python
Python 获取项目根路径的代码
2019/09/27 Python
python不到50行代码完成了多张excel合并的实现示例
2020/05/28 Python
Pytorch mask-rcnn 实现细节分享
2020/06/24 Python
Python实现区域填充的示例代码
2021/02/03 Python
详解canvas.toDataURL()报错的解决方案全都在这了
2020/03/31 HTML / CSS
马来西亚网上购物平台:ezbuy
2018/02/13 全球购物
英国健身仓库:Bodybuilding Warehouse
2019/03/06 全球购物
中软Java笔试题
2012/11/11 面试题
中药专业大学生医药工作求职信
2013/10/25 职场文书
食堂个人先进事迹
2014/01/22 职场文书
公务员政审个人鉴定
2014/02/25 职场文书
节约粮食标语
2014/06/18 职场文书
2014年环境整治工作总结
2014/12/10 职场文书
Golang 遍历二叉树
2022/04/19 Golang