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 相关文章推荐
python 实现插入排序算法
Jun 05 Python
用smtplib和email封装python发送邮件模块类分享
Feb 17 Python
Python警察与小偷的实现之一客户端与服务端通信实例
Oct 09 Python
python版opencv摄像头人脸实时检测方法
Aug 03 Python
一文带你了解Python中的字符串是什么
Nov 20 Python
python 输出所有大小写字母的方法
Jan 02 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
Feb 19 Python
Django Channel实时推送与聊天的示例代码
Apr 30 Python
pycharm 添加解释器的方法步骤
Aug 31 Python
Python模拟登录requests.Session应用详解
Nov 17 Python
PyChon中关于Jekins的详细安装(推荐)
Dec 28 Python
Python基础数据类型tuple元组的概念与用法
Aug 02 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中的字符串函数
2006/10/09 PHP
理解php原理的opcodes(操作码)
2010/10/26 PHP
php数组函数序列之array_slice() - 在数组中根据条件取出一段值,并返回
2011/11/07 PHP
Zend Framework连接Mysql数据库实例分析
2016/03/19 PHP
PHP错误机制知识汇总
2016/03/24 PHP
YII2框架中使用yii.js实现的post请求
2017/04/09 PHP
jQuery第三课 修改元素属性及内容的代码
2010/03/14 Javascript
js 如何实现对数据库的增删改查
2012/11/23 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
基于Bootstrap+jQuery.validate实现Form表单验证
2014/12/16 Javascript
js分页工具实例
2015/01/28 Javascript
jQuery实现平滑滚动页面到指定锚点链接的方法
2015/07/15 Javascript
基于JavaScript代码实现自动生成表格
2016/06/15 Javascript
js计算系统当前日期是星期几的方法
2016/07/14 Javascript
JS 对java返回的json格式的数据处理方法
2016/12/05 Javascript
利用jquery禁止外层滚动条的滚动
2017/01/05 Javascript
通过实例解析js简易模块加载器
2019/06/17 Javascript
JS实现悬浮球只在一侧滑动并且是横屏状态下
2020/08/19 Javascript
[05:48]DOTA2英雄梦之声vol21 屠夫
2014/06/20 DOTA
[03:57]《不朽》——2015DOTA2国际邀请赛—中国军团出征主题曲MV
2015/07/15 DOTA
python实现在控制台输入密码不显示的方法
2015/07/02 Python
ansible作为python模块库使用的方法实例
2017/01/17 Python
python验证码识别教程之灰度处理、二值化、降噪与tesserocr识别
2018/06/04 Python
python hook监听事件详解
2018/10/25 Python
使用python3调用wxpy模块监控linux日志并定时发送消息给群组或好友
2019/06/05 Python
Python 实现数据结构-堆栈和队列的操作方法
2019/07/17 Python
moosejaw旗下的户外商品促销网站:Mountain Steals
2017/02/27 全球购物
英国现代绅士品牌:Hackett
2017/12/17 全球购物
编程用JAVA解析XML的方式
2013/07/07 面试题
【魔兽争霸3重制版】原版画面与淬火MOD画面对比
2021/03/26 魔兽争霸
教育科研先进个人材料
2014/01/26 职场文书
2014年幼儿园德育工作总结
2014/12/17 职场文书
党风廉政建设心得体会(2016最新版)
2016/01/22 职场文书
python基础之类方法和静态方法
2021/10/24 Python
python画条形图的具体代码
2022/04/20 Python
Python 数据可视化工具 Pyecharts 安装及应用
2022/04/20 Python