python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > python调用C/C++

在python中调用C/C++的三种方法

作者:哦豁灬

这篇文章主要给大家介绍了关于在python中调用C/C++的三种方法,Python可以通过调用C/C++接口来实现与C/C++语言的交互,文中通过代码介绍的非常详细,需要的朋友可以参考下

Python 是一种很好用的胶水语言,利用Python的简洁和C++的高效,基本可以解决99%的问题了,剩下那 1% 的问题也就不是问题了,毕竟不是所有问题都可解。

一般的,Python和C++的交互分为这两种情况:

这里讨论前者,在 python 中调用 C/C++ 代码的方法很多,这里记录三种方法的使用。

1 C/C++ 编译成可执行文件,python 通过 subprocess 调用

C/C++ 代码正常编写,然后编译成 exe/elf 格式的可执行文件,Python 利用 subprocess 调用该可执行文件即可。好处是改动小,不好是至少需要两个进程跑代码,而且 C/C++ 和 Python 通讯比较麻烦。

这种方法简单粗暴,不太好用,没什么好说的。

2 ctypes

C/C++ 在编写代码的时候略微改动,然后编译成 dll/so 格式的动态库文件,Python 利用 ctypes 调用该库文件即可。好处一个进程内运行,C/C++ 侧改动小,坏处是 Python 侧需适配代码比较多。

ctypes 是 python 自带的一个库,可以用来调用 c/cpp 的动态链接库。使用 ctypes 调用 c++ 代码步骤如下:

2.1 编译 C++

一个简单的 demo:

dll.cpp

extern "C" int add(int a, int b) {
	return a + b;
}

在目录 python_call_c_cpp 下,使用 g++ 编译 dll.cpp

g++ --shared -fPIC dll.cpp -o libadd.so

编译完成后,在目录下会生成一个 libadd.so 文件:

2.2 python 调用 C/C++ 库

main.py

import ctypes

# 加载动态链接库 
lib = ctypes.cdll.LoadLibrary("./libadd.so") 
# 定义函数参数类型和返回值类型 
lib.add.argtypes = [ctypes.c_int, ctypes.c_int] 
lib.add.restype = ctypes.c_int 

# 调用 C++ 函数 
result = lib.add(1, 2) 
print("调用C++库的结果:" + str(result))

执行 python3 main.py:

3 Boost.Python

Boost作为一个大宝库,提供了我们所需要的这一功能。并且,在Boost的许多库中,已经默认使用了Boost.Python,所以也算是经过了充分的测试。

3.1 安装

Boost的大部分功能都是以头文件的形式提供的,无需安装;但是也有少部分功能,需要进行手动编译。Boost.Python 需要进行手动编译。

3.2 一个简单的 demo

用C++实现一个模块,在Python中调用时,可以返回一个特定的字符串。

#include <boost/python.hpp>

char const* greet()
{
	return "hello, boost";
}

BOOST_PYTHON_MODULE(hello_boostpy)
{
	using namespace boost::python;
	def("greet", greet);
}

将其编译成动态链接库的形式:

g++ -I /usr/include/python2.7/ -fPIC -shared -o hello_boostpy.so http://hello_boostpy.cc -lboost_python

这时可以使用ldd看看hello_boostpy.so可不可以找到libboost_python,找不到的话,需要手动将其路径加入环境变量LD_LIBRARY_PATH中,或者用ldconfig相关的命令也可以。

在Python中使用hello_boostpy库:

# -*- coding: utf-8 -*-
import sys
sys.path.append('.')

def test():
    import hello_boostpy
    return hello_boostpy.greet()

if __name__ == "__main__":
    print test()

接下来,我们在C++实现的模块中,添加一个类,并且尝试向C++方向传入Python的list类型对象。

C++ 类:

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

struct Person
{
	void set_name(std::string name) { this->name = name; }
	std::string print_info();
	void set_items(list& prices, list& discounts);
	
	
	std::string name;
	std::vector<double> item_prices;
	std::vector<double> item_discounts;
};

其中,Python方的list类型,在Boost.Python中有一个对应的实现boost::python::list(相应的,dict、tuple等类型都有对应实现)。在set_items中,我们将会用boost::python::extract对数据类型做一个转换。

void Person::set_items(list& prices, list& discounts)
{
	for(int i = 0; i < len(prices); ++i)
	{
		double price = extract<double>(prices[i]);
		double discount = extract<double>(discounts[i]);
		item_prices.push_back(price);
		item_discounts.push_back(discount);
	}
}

Python模块定义部分依旧是非常直观的代码:

BOOST_PYTHON_MODULE(person)
{
	class_<Person>("Person")
		.def("set_name", &Person::set_name)
		.def("print_info", &Person::print_info)
		.def("set_items", &Person::set_items)
	;	
}

在Python代码中,就可以像使用一个Python定义的类一样使用Person类了:

# -*- coding: utf-8 -*-
import sys
sys.path.append('.')

def test():
    import person
    p = person.Person()
    p.set_name('Qie')
    p.set_items([100, 123.456, 888.8], [0.3, 0.1, 0.5])
    print p.print_info()

if __name__ == "__main__":
    test()

附c++调用Python:

将Python安装目录下的include和libs文件夹引入到项目中

将libs目录下的python37.lib复制一份为python37_d.lib

1、Python脚本

def Hello():
    print("Hello")
     
def Add(a,b):
    return  a+b

2、C++调用python脚本

#include <Python.h>
using namespace std;
 
int main()
{
    Py_Initialize();              //初始化,创建一个Python虚拟环境
    if (Py_IsInitialized())
    {
        PyObject* pModule = NULL;
        PyObject* pFunc = NULL;
        pModule = PyImport_ImportModule("test_python");  //参数为Python脚本的文件名
        if (pModule)
        {
            pFunc = PyObject_GetAttrString(pModule, "Hello");   //获取函数
            PyEval_CallObject(pFunc, NULL);           //执行函数
        }
        else
        {
            printf("导入Python模块失败...\n");
        }
    }
    else
    {
        printf("Python环境初始化失败...\n");
    }
    Py_Finalize();
}

接口方法

Python3.6提供给C/C++接口函数,基本都是定义pylifecycle.h,pythonrun.h,ceval.h中。

3、规范化语法

#include<Python.h> //添加python的声明
 
using namespace std;
 
int main()
{
Py_Initialize(); //1、初始化python接口
 
//初始化使用的变量
PyObject* pModule = NULL;
PyObject* pFunc = NULL;
PyObject* pName = NULL;
 
//2、初始化python系统文件路径,保证可以访问到 .py文件
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
 
//3、调用python文件名。当前的测试python文件名是test.py。在使用这个函数的时候,只需要写文件的名称就可以了。不用写后缀。
pModule = PyImport_ImportModule("test");
 
//4、调用函数
pFunc = PyObject_GetAttrString(pModule, "AdditionFc");
 
//5、给python传参数
PyObject* pArgs = PyTuple_New(2);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数。如果AdditionFc中只有一个参数时,写1就可以了。这里只先介绍函数必须有参数存在的情况。
 
 
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); //0:表示序号。第一个参数。
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); //1:也表示序号。第二个参数。i:表示传入的参数类型是int类型。
 
//6、使用C++的python接口调用该函数
PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
 
//7、接收python计算好的返回值
int nResult;
PyArg_Parse(pReturn, "i", &nResult);//i表示转换成int型变量。在这里,最需要注意的是:PyArg_Parse的最后一个参数,必须加上“&”符号。
 
//8、结束python接口初始化
Py_Finalize();
}

总结 

到此这篇关于在python中调用C/C++的三种方法的文章就介绍到这了,更多相关python调用C/C++内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文