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中cPickle用法例子分享
Jan 03 Python
Python实现的矩阵类实例
Aug 22 Python
python 获取utc时间转化为本地时间的方法
Dec 31 Python
pyqt5实现登录界面的模板
May 30 Python
kali中python版本的切换方法
Jul 11 Python
基于sklearn实现Bagging算法(python)
Jul 11 Python
处理python中多线程与多进程中的数据共享问题
Jul 28 Python
python3 selenium自动化 下拉框定位的例子
Aug 23 Python
通过 Django Pagination 实现简单分页功能
Nov 11 Python
Django通用类视图实现忘记密码重置密码功能示例
Dec 17 Python
Python基础之字符串操作常用函数集合
Feb 09 Python
Python实现邮件发送的详细设置方法(遇到问题)
Jan 18 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
是否存在第一台收音机的说法
2021/03/01 无线电
php中0,null,empty,空,false,字符串关系的详细介绍
2013/06/20 PHP
PHP获取和操作配置文件php.ini的几个函数介绍
2013/06/24 PHP
函数中使用require_once问题深入探讨 优雅的配置文件定义方法推荐
2014/07/02 PHP
ThinkPHP验证码和分页实例教程
2014/08/22 PHP
PHP实现事件机制的方法
2015/07/10 PHP
PHP通过加锁实现并发情况下抢码功能
2016/08/10 PHP
CI框架常用函数封装实例
2016/11/21 PHP
jQuery 遍历-nextUntil()方法以及prevUntil()方法的使用介绍
2013/04/26 Javascript
H5基于iScroll实现下拉刷新和上拉加载更多
2017/07/18 Javascript
JS实现移动端触屏拖拽功能
2018/07/31 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
JSON的parse()方法介绍
2019/01/31 Javascript
微信小程序用户授权,以及判断登录是否过期的方法
2019/05/10 Javascript
JavaScript中BOM对象原理与用法分析
2019/07/09 Javascript
[02:29]DOTA2英雄基础教程 陈
2013/12/17 DOTA
Python操作列表的常用方法分享
2014/02/13 Python
Python中统计函数运行耗时的方法
2015/05/05 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
Python异常处理操作实例详解
2018/08/28 Python
详解python while 函数及while和for的区别
2018/09/07 Python
Python  Django 母版和继承解析
2019/08/09 Python
Python面向对象之私有属性和私有方法应用案例分析
2019/12/31 Python
基于TensorFlow常量、序列以及随机值生成实例
2020/01/04 Python
Jupyter notebook如何实现指定浏览器打开
2020/05/13 Python
Pycharm Git 设置方法
2020/09/15 Python
python修改微信和支付宝步数的示例代码
2020/10/12 Python
使用Django的JsonResponse返回数据的实现
2021/01/15 Python
html5移动端自适应布局的实现
2020/04/15 HTML / CSS
html5关于外链嵌入页面通信问题(postMessage解决跨域通信)
2020/07/20 HTML / CSS
北美三大旅游网站之一:Travelocity
2017/08/12 全球购物
公司承诺书格式
2014/05/21 职场文书
自我检讨报告
2015/01/28 职场文书
雷锋的故事观后感
2015/06/10 职场文书
受欢迎的自荐信,就这么写!
2019/04/19 职场文书
nginx作grpc的反向代理踩坑总结
2021/07/07 Servers