Python本身有着C接口,可以用C语言编写扩展模块,提供这个扩展的就是pybind11
,有了它我们就能使用C++来开发Python扩展了。
首先我们需要在centos环境下安装这个库,使用以下命令:
sudo yum -y install python3-devel
sudo yum -y install python3-pip
pip3 install pytest
pip3 install pybind11
ubuntu环境下使用以下命令:
sudo apt-get install python3-dev
sudo apt-get install python3-pip
pip3 install pytest
pip3 install pybind11
在C++中,只需要使用一个宏PYBIND11_MODULE
,其宏原型如下:
/**
* @brief python 扩展模块
* moudle_name 扩展名称,可以使用python语法import导入
* module python::module实例对象,封装了所有操作
*/
PYBIND11_MODULE(module_name,module); // 定义Python模块
一个简单的例子如下,其定义了一个C++函数info
,其会打印C++的环境信息。然后我们将其添加到pybind11之中。另外再定义一个有参的函数add
,其计算两数之和。
#include
namespace py = pybind11; // 名字空间别名,简化代码
PYBIND11_MODULE(pydemo, m) // 定义Python模块pydemo
{
m.def("info", // 定义Python函数
[]() // 定义一个lambda表达式
{
py::print("c++ version =", __cplusplus); // pybind11自己的打印函数
py::print("gcc version =", __VERSION__);
py::print("libstdc++ =", __GLIBCXX__);
});
m.def("add", // 定义Python函数
[](int a, int b) // 有参数的lambda表达式
{
return a + b;
});
} // Python模块定义结束
之后,我们使用如下代码进行编译:
g++ main.cpp -std=c++11 -shared -fPIC `python3 -m pybind11 --includes` -o pydemo`python3-config --extension-suffix`
这个编译代码较为复杂,我们来解释以下:
# 最后" "编译出来的文件"`python3-xxxxxxxx "不要有空格,这个是规约,要不python找不到扩展
gcc "需要编译的文件" -std=c++11 -shared -fPIC `python3 -m pybind11 --includes` -o "编译出来的文件"`python3-config --extension-suffix`
最后,我们编写如下的python脚本:
import pydemo
pydemo.info()
x=pydemo.add(1,2)
print(x)
测试结果如下:
[ik@localhost test]$ python3 test.py
c++ version = 201103
gcc version = 7.5.0
libstdc++ = 20191114
3
pybind11也支持函数的参数和返回值使用标准容器,会自动转换成python中的list和dict。不过需要包含头文件stl.h
下面是一个例子:
#include
#include
#include
using namespace std;
namespace py = pybind11;
PYBIND11_MODULE(pydemo,module)
{
module.def("use_str",
[](const string &str)
{
py::print(str);
return str + "!";
});
module.def("use_tuple",
[](tuple<int, int, string> tu)
{
get<0>(tu)++;
get<1>(tu)++;
get<2>(tu) += "?";
return tu;
});
module.def("use_list",
[](vector<int> &vec)
{
py::print("input: ", vec);
vec.push_back(100);
return vec;
});
}
python直接调用C++写好的函数就可以了:
import pydemo
list_=[0,1,2,3]
# use_list 传入list被转换成vector
ret_list = pydemo.use_list(list_)
print("ret list: "+str(ret_list))
str_="hello world"
ret_str = pydemo.use_str(str_)
print("ret str: "+ret_str)
tuple_=(0,1,"hello")
ret_tuple=pydemo.use_tuple(tuple_)
print("ret tuple: "+str(ret_tuple))
结果如下:
[ik@localhost test]$ python3 test.py
input: [0, 1, 2, 3]
ret list: [0, 1, 2, 3, 100]
hello world
ret str: hello world!
ret tuple: (1, 2, 'hello?')
C++中的类也可以等价转换到Python中进行调用,这需要用到一个特别的模板类class_。下面是个例子:
#include
namespace py = pybind11;
class Point final
{
public:
Point(int a)
{
x = a;
}
Point()
{
Point(0);
}
public:
int get() { return x; }
void set(int a)
{
x = a;
}
private:
int x;
};
PYBIND11_MODULE(pydemo, m)
{
py::class_<Point>(m, "Point")
.def(py::init())
.def(py::init<int>())
.def("get", &Point::get)
.def("set", &Point::set);
};
python中直接调用就可以了:
import pydemo
point = pydemo.Point(1);
print(point.get())
最终输出结果如下:
[ik@localhost test]$ python3 test.py
1
[1] 罗剑锋.罗剑锋的C++实战笔记.极客时间