pybind11学习笔记
介绍
pybind11是一个轻量级的仅含头文件的C++库,用简单的语法为Python创建拓展模块,以加速运行。官方介绍可参考官方文档。
安装
Windows端
- 环境配置
- Windows 10 家庭版 64位
- Python 3.8.0 [MSC v.1916 32 bit (Intel)] on win32
- gcc (MinGW.org GCC-8.2.0-3) 8.2.0
- g++ (MinGW.org GCC-8.2.0-3) 8.2.0
- pybind11 v2.6.2
- 从官方仓库的发布页下载pybind11的源码。
- 解压下载的压缩包,将其中的
include
文件夹单独放置。Linux端
- 在Baidu AI Studio上注册帐户并登录。
- 点击上方菜单栏中的[项目],点击[创建项目]。
- 选择类型:选择[Notebook]类型,点击[下一步]。
- 配置环境:项目框架选择[PaddlePaddle 2.0.0],项目环境选择[python 3.7],点击[下一步]。
- 项目环境:三个项目均可随意设置,点击[创建]。
- 启动环境:选择[基础版 (免费使用)],点击[确定]。
- 待提示“环境启动成功”后点击[进入],进入环境。
- 点击菜单栏下方的[终端-1],切换至终端选项卡,并依次执行以下命令:
1
2
3mkdir pybind
cd pybind
git clone https://github.com.cnpmjs.org/pybind/pybind11.git
编写测试程序
Windows端
- 在
include
文件夹下创建myetyet.cpp
文件,编写如下内容后保存。1
2
3
4
5
6
7
8
9
10
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(myetyet, m) {
m.doc() = "myetyet's pybind11 test module";
m.def("add", &add, "A function which adds two integers.");
} - 重复上述过程,创建
setup.py
文件,编写如下内容后保存。1
2
3from setuptools import setup, Extension
setup(ext_modules=[Extension(name="myetyet", sources=["myetyet.cpp"], include_dirs=["."])]) - 在终端依次执行以下命令:
1
2python setup.py build_ext -c mingw32 --inplace
dir | find "pyd" - 若终端给出形如
myetyet.cp38-win32.pyd
的信息,即代表测试程序编译完成。 - 继续在终端依次执行
python
命令。 - 在Python交互式终端中依次执行以下语句:
1
2
3
4import os
os.add_dll_directory("C:\\MinGW\\bin")
import myetyet
myetyet.add(233, 666) - 注意,此处的
C:\\MinGW\\bin
应为pyd
文件依赖的动态链接库的所在目录,一般为MinGW安装目录的bin
文件夹。 - 若终端先后给出
<AddedDllDirectory('C:\\MinGW\\bin')>
和899
的信息,即代表测试程序可以正确运行。 - 在Python交互式终端中输入
exit()
退出。
Linux端
- 点击左侧侧边栏上方第四个按钮[刷新]。
- 随后将在侧边栏中刷新出刚创建的
pybind
文件夹,点击之进入。 - 点击上方第一个按钮[新建文件],名称为
myetyet.cpp
,点击[确定]。 - 点击侧边栏中的
myetyet.cpp
,在右侧代码编辑区中输入以下代码后保存。1
2
3
4
5
6
7
8
9
10
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(myetyet, m) {
m.doc() = "myetyet's pybind11 test module";
m.def("add", &add, "A function which adds two integers.");
} - 重复上述过程,创建
CMakeLists.txt
文件,输入以下代码后保存。1
2
3
4
5cmake_minimum_required(VERSION 3.4)
project(myetyet LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
add_subdirectory(pybind11)
pybind11_add_module(myetyet myetyet.cpp) - 在终端依次执行以下命令:
1
2
3
4
5mkdir build
cd build
cmake ..
make
ls | grep so - 若终端给出形如
myetyet.cpython-37m-x86_64-linux-gnu.so
的信息,即代表测试程序编译完成。 - 继续在终端依次执行
python
命令。 - 在Python交互式终端中依次执行以下语句:
1
2import myetyet
myetyet.add(233, 666) - 若终端给出
899
的信息,即代表测试程序可以正确运行。 - 在Python交互式终端中输入
exit()
退出。 - 方便起见,在
pybind
文件夹下创建build.sh
。在终端依次执行以下命令:1
2
3cd ~/pybind
echo -e "if [ ! -d \"./build\" ]; then\n mkdir build\nfi" > build.sh
echo -e "cd build\ncmake ..\nmake\ncp *.so ../" >> build.sh - 之后对
myetyet.cpp
进行修改后则可在pybind
文件夹下执行sh build.sh
命令进行自动编译并复制.so
文件至该目录。 - 执行
python
命令进入Python交互式终端后,则可直接使用import myetyet
进行模块的导入。
pybind11语法
基础
- 引用头文件及命名空间
- C++侧
1
2
namespace py = pybind11;
- C++侧
- 创建模块
- C++侧
1
PYBIND11_MODULE(module_name, m) { ... }
- 备注
- 严格意义上该条语法并不是函数定义,而是宏定义。
- 参数
module_name
:模块名(切勿加引号)m
:主要接口(py::module_
型变量)
- C++侧
- 属性
- C++侧
1
2
3
4
5
6
7
8
9
10
11// Normal types of variables exporting
m.attr("a_bool") = true;
m.attr("a_char") = 'A';
m.attr("a_float") = 6.66;
m.attr("an_int") = 233;
m.attr("a_string") = py::cast("myetyet");
// Include in advance
// STL container variables exporting
m.attr("a_vector") = py::cast(std::vector<int> {233, 666}); - Python侧
1
2
3
4
5
6
7from myetyet import *
a_bool, type(a_bool) # Output: (True, <class 'bool'>)
a_char, type(a_char) # Output: ('A', <class 'str'>)
a_float, type(a_float) # Output: (6.66, <class 'float'>)
an_int, type(an_int) # Output: (233, <class 'int'>)
a_string, type(a_string) # Output: ('myetyet', <class 'str'>)
a_vector, type(a_vector) # Output: ([233, 666], <class 'list'>)
- C++侧
- 枚举类型
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13// Structure wrapper and inner enumerations definition
struct Enums {
enum DialogResult { Cancel = 2, OK = 0, Abort, Retry = 4 };
};
// Structure wrapper exporting
py::class_<Enums> pyEnums(m, "Enums");
// Enumerations exporting
py::enum_<Enums::DialogResult>(pyEnums, "DialogResult")
.value("Cancel", Enums::DialogResult::Cancel)
.value("OK", Enums::DialogResult::OK)
.value("Abort", Enums::DialogResult::Abort)
.value("Retry", Enums::DialogResult::Retry)
.export_values(); - Python侧
1
2
3
4
5
6
7
8
9
10
11
12from myetyet import *
Enums.DlgRes.OK # Output: <DlgRes.OK: 0>
str(Enums.DlgRes.Abort) # Output: 'DlgRes.Abort'
int(Enums.DlgRes.Cancel) # Output: 2
from pprint import pprint
pprint(Enums.DlgRes.__members__) # Output as follows:
"""
{'Abort': <DlgRes.Abort: 1>,
'Cancel': <DlgRes.Cancel: 2>,
'OK': <DlgRes.OK: 0>,
'Retry': <DlgRes.Retry: 4>}
"""
- C++侧
- 引用头文件及命名空间
函数
简单函数
- C++侧
1
2
3
4
5
6
7
8// Function definition
int add(int i, int j) {
return i + j;
}
// Function exporting with referencing a defined function
m.def("add", &add, "A function which adds two integers.");
// Function exporting with a lambda function
m.def("mod", [](int i, int j) { return i % j; }, "C++ mod function"); - Python侧
1
2
3
4
5
6
7
8
9
10
11help(myetyet.mod) # Output as follows:
"""
Help on built-in function mod in module myetyet:
mod(...) method of builtins.PyCapsule instance
mod(arg0: int, arg1: int) -> int
C++ mod function
"""
myetyet.add(666, 233) # Output: 899
myetyet.mod(666, 233) # Output: 200
- C++侧
关键字参数
- C++侧
1
2
3
4
5
6
7
8
9// Function definition
int _xor(int x, int y) {
return x ^ y;
}
// Regular notation
m.def("xor", &_xor, py::arg("x"), py::arg("y"));
// Shorthand
using namespace pybind11::literals;
m.def("xor_", &_xor, "x"_a, "y"_a); - Python侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17help(myetyet.div) # Output as follows:
"""
Help on built-in function xor in module myetyet:
xor(...) method of builtins.PyCapsule instance
xor(x: int, y: int) -> int
"""
myetyet.xor(x=666, y=233) # Output: 627
myetyet.xor(y=233, x=666) # Output: 627
help(myetyet.xor) # Output as follows:
"""
Help on built-in function xor_ in module myetyet:
xor_(...) method of builtins.PyCapsule instance
xor_(x: int, y: int) -> int
"""
myetyet.xor_(666, y=233) # Output: 627
- C++侧
参数默认值
- C++侧
1
2
3
4
5// Regular notation
m.def("xor", &_xor, py::arg("x") = 666, py::arg("y") = 233);
// Shorthand
using namespace py::literals;
m.def("xor_", &_xor, "x"_a = 666, "y"_a = 233); - Python侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17help(myetyet.xor) # Output as follows:
"""
Help on built-in function xor in module myetyet:
xor(...) method of builtins.PyCapsule instance
xor(x: int = 666, y: int = 233) -> int
"""
myetyet.xor(y=666) # Output: 0
myetyet.xor(233) # Output: 0
help(myetyet.xor_) # Output as follows:
"""
Help on built-in function xor_ in module myetyet:
xor_(...) method of builtins.PyCapsule instance
xor_(x: int = 666, y: int = 233) -> int
"""
myetyet.xor_() # Output: 627
- C++侧
重载
- C++侧
1
2
3
4
5
6
7
8
9
10
11// Function definition
double add(double x, double y) { return x + y; }
string add(string x, string y) { return x + y; }
// Function exporting with a lambda function
m.def("add", [](int x, int y){ return x + y; }, "x"_a, "y"_a);
// Function exporting with referencing a defined function
// C++ 11 style
m.def("add", static_cast<double (*)(double, double)>(&add), "x"_a, "y"_a);
// C++ 14 style
m.def("add", py::overload_cast<string, string>(&add), "x"_a, "y"_a); - Python侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17help(myetyet.add) # Output as follows:
"""
Help on built-in function add in module myetyet:
add(...) method of builtins.PyCapsule instance
add(*args, **kwargs)
Overloaded function.
1. add(x: float, y: float) -> float
2. add(x: str, y: str) -> str
3. add(x: int, y: int) -> int
"""
myetyet.add(2.33, 6.66) # Output: 8.99
myetyet.add(233, 666) # Output: 899
myetyet.add("myet", "yet") # Output: 'myetyet'
- C++侧
自定义类
构造函数与简单函数
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// Include in advance
using namespace std;
// Structure definition
struct Person {
string name;
Person(const string &name) : name(name) { }
const string &getName() const { return name; }
void setName(const string &name) { this->name = name; }
};
// Structure exporting
py::class_<Person>(m, "Person")
.def(py::init<const string &>(), "Initialize", "name"_a = "")
// Function exporting with referencing a defined function
.def("getName", &Person::getName, "Get name")
.def("setName", &Person::setName, "Set name", "name"_a);
// Function exporting with a lambda function
.def("__repr__", [](const Person &p) { return "<a Person named '" + p.name + "'>"; }); - Python侧
1
2p = myetyet.Person("123"); p # Output: <a Person named '123'>
p.setName("myetyet"); p.getName() # Output: 'myetyet'
- C++侧
属性
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// Structure definition
struct Person {
string name;
const string ID = "233666";
Person(const string &name) : name(name) { }
const string &getName() const { return name; }
void setName(const string &name) { this->name = name; }
const string &getID() const { return ID; }
};
// Structure exporting
py::class_<Person>(m, "Person")
// Read/Write property
.def_readwrite("name", &Person::name)
// Or apply following 1 choice when a property is not public
// .def_property("name", &Person::getName, &Person::setName)
// Readonly property
.def_readonly("ID", &Person::ID)
// Or apply following 2 choices when a property is not public
// .def_property("ID", &Person::getID, nullptr)
// .def_property_readonly("ID", &Person::getID)
.def(py::init<const string &>(), "Initialize", "name"_a = ""); - Python侧
1
2
3
4
5
6
7
8
9p = myetyet.Person("myetyet")
p.name, p.ID # Output: ('myetyet', '233666')
p.name = "myetyet_"; p.name # Output: 'myetyet_'
p.ID = "666666" # Output as follows:
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
"""
- C++侧
动态属性
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14// Class definition
class Person {
public:
Person(const string &name) : name(name) { }
const string &getName() const { return name; }
void setName(const string &name) { this->name = name; }
private:
string name;
const string ID = "233666";
};
// Class exporting
py::class_<Person>(m, "Person", py::dynamic_attr())
.def_readwrite("name", &Person::name)
.def(py::init<const string &>(), "Initialize", "name"_a = ""); - Python侧
1
2
3p = myetyet.Person("myetyet")
p.ID = 233666; p.url = "https://myetyet.js.org/"
p.__dict__ # Output: {'ID': 233666, 'url': 'https://myetyet.js.org/'}
- C++侧
继承
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// Structure definition
struct Person {
string name;
Person(const string &name) : name(name) { }
};
struct Student : Person {
Student(const string &name): Person{name} { }
string loaf() { return "mo yu"; }
};
// Structure exporting
py::class_<Person>(m, "Person")
.def_readwrite("name", &Person::name)
.def(py::init<const string &>(), "Initialize Person", "name"_a = "");
py::class_<Student, Person>(m, "Student")
.def("loaf", &Student::loaf)
.def(py::init<const string &>(), "Initialize Student", "name"_a = ""); - Python侧
1
2p = Student("myetyet")
p.name, p.loaf() # Output: ('myetyet', 'mo yu')
- C++侧
函数重载
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// Structure definition
struct Vector2 {
double x, y;
Vector2() : x(0.0), y(0.0) {}
Vector2(double x, double y) : x(x), y(y) {}
void set(char lbl, double val) { switch (lbl) { case 'x': x = val; break; case 'y': y = val; break; } }
void set(double x, double y) { this->x = x; this->y = y; }
};
// Structure exporting
py::class_<Vector2>(m, "Vector2")
// Constructor overloading
.def(py::init<>())
.def(py::init<double, double>(), "x"_a, "y"_a)
// Member function overloading
// C++ 11 style
.def("set", static_cast<void (Vector2::*)(double, double)>(&Vector2::set), "x"_a, "y"_a)
// C++ 14 style
.def("set", py::overload_cast<char, double>(&Vector2::set), "label"_a, "value"_a)
// Class display info
.def("__repr__", [](const Vector2 &v) { return "<2D Vector (" + to_string(v.x) + ", " + to_string(v.y) + ")>"; }); - Python侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29help(myetyet.Vector2.__init__) # Output as follows:
"""
Help on instancemethod in module myetyet:
__init__(...)
__init__(*args, **kwargs)
Overloaded function.
1. __init__(self: myetyet.Vector2) -> None
2. __init__(self: myetyet.Vector2, x: float, y: float) -> None
"""
v = myetyet.Vector2(); v # Output: <2D Vector (0.000000, 0.000000)>
v = myetyet.Vector2(.1, .2); v # Output: <2D Vector (0.100000, 0.200000)>
help(myetyet.Vector2.set) # Output as follows:
"""
Help on instancemethod in module myetyet:
set(...)
set(*args, **kwargs)
Overloaded function.
1. set(self: myetyet.Vector2, x: float, y: float) -> None
2. set(self: myetyet.Vector2, label: str, value: float) -> None
"""
v.set(6.66, 2.33); v # Output: <2D Vector (6.660000, 2.330000)>
v.set("x", 6.1234575); v # Output: <2D Vector (6.123457, 2.330000)>
v.set("y", 6.1234585); v # Output: <2D Vector (6.123457, 6.123458)>
- C++侧
重载运算符
- C++侧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// Structure definition
struct Vector2 {
double x, y;
Vector2(double x, double y) : x(x), y(y) {}
Vector2 operator-() const { return Vector2(-x, -y); }
Vector2 operator*(double k) const { return Vector2(k * x, k * y); }
Vector2 &operator*=(double k) { x *= k; y *= k; return *this; }
double operator*(const Vector2 &b) const { return x * b.x + y * b.y; }
friend Vector2 operator*(double k, const Vector2 &v) { return Vector2(k * v.x, k * v.y); }
};
// Structure exporting
py::class_<Vector2>(m, "Vector2")
.def(py::init<double, double>(), "x"_a, "y"_a)
.def(-py::self)
.def(py::self * double())
.def(py::self *= double())
.def(py::self * py::self)
.def(double() * py::self)
.def("__add__", [](const Vector2 &a, const Vector2 &b) { return Vector2(a.x + b.x, a.y + b.y); }, py::is_operator())
.def("__repr__", [](const Vector2 &v) { return "<2D Vector (" + to_string(v.x) + ", " + to_string(v.y) + ")>"; }); - Python侧
1
2
3
4
5
6
7
8
9a = myetyet.Vector2(3, 4)
-a # Output: <2D Vector (-3.000000, -4.000000)>
a * 2 # Output: <2D Vector (6.000000, 8.000000)>
a *= -2.0; a # Output: <2D Vector (-6.000000, -8.000000)>
b = myetyet.Vector2(1, 2)
a * b # Output: -22.0
-.3 * a # Output: <2D Vector (1.800000, 2.400000)>
a + b # Output: <2D Vector (-5.000000, -6.000000)>
2 * a + b * 3 # Output: <2D Vector (-9.000000, -10.000000)>
- C++侧
- 本文标题:pybind11学习笔记
- 本文作者:myetyet
- 创建时间:2021-02-01 13:40:56
- 本文链接:https://myetyet.github.io/posts/dc90fd13/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
评论