pybind11 embedded in C++: C++内使用pybind11功能
C++内使用pybind11嵌入功能C++源码内可以使用pybind11嵌入方式提供的功能:构建Python解释器,执行Python命令、脚本,构造pybind11嵌入模块,加载调用Python模块等。源码来源于pybind11使用手册。示例源码(embed.cpp)#include<pybind11/embed.h>#include<iostream>#include&l
文章目录
C++内使用pybind11嵌入功能
C++源码内可以使用pybind11嵌入方式提供的功能:构建Python解释器,执行Python命令、脚本,构造pybind11嵌入模块,加载调用Python模块等。源码来源于pybind11使用手册。
借用此方式,可以在C++代码中使用Python特色模块。同时需要考虑安全问题,防止一些恶意代码注入。
示例源码(embed.cpp)
#include<pybind11/embed.h>
#include<iostream>
#include<string>
#include<cassert>
namespace py=pybind11;
using namespace py::literals;
//定义多个embedded模块
PYBIND11_EMBEDDED_MODULE(fast_calc,m){
m.def("add",[](int i,int j){
return i+j;
});
}
PYBIND11_EMBEDDED_MODULE(cpp_module,m){
m.attr("a")=1;
}
int main(int argc, char **argv)
{
//-----------------------------------------------------------------
//启动Python解释器
py::scoped_interpreter guard{};
//-------------------------------------------------------------------
//调用Python函数
py::print("Hello,World");
//-------------------------------------------------------------------
//执行Python脚本
py::exec(R"(
kwargs=dict(name="world",number=42)
message="Hello,{name}! The answer is {number}".format(**kwargs)
print(message)
)");
//--------------------------------------------------------------------
auto kwargs=py::dict("name"_a="world","number"_a=42);
auto message="Hello,{name}! The answer is {number}"_s.format(**kwargs);
py::print(message);
//-------------------------------------------------------------------
//初始化脚本空间状态
auto locals=py::dict("name"_a="world","number"_a=42);
//运行脚本
py::exec(R"(
message="Hello,{name}! The answer is {number}".format(**locals())
)",py::globals(),locals);
std::cout<<locals["message"].cast<std::string>()<<std::endl;
//---------------------------------------------------------------------
//加载Python模块,获取模块变量(py::object)
auto sys=py::module_::import("sys");
py::print(sys.attr("path"));
//---------------------------------------------------------------------
//加载pybind11嵌入模块,运行命令
auto calc=py::module_::import("calc");
auto result=calc.attr("add")(1,2);
int n=result.cast<int>();
assert(n==3);
//-------------------------------------------------------------------------
{
//加载embedded模块
auto fast_calc=py::module_::import("fast_calc");
auto result=fast_calc.attr("add")(1,2).cast<int>();
assert(result==3);
}
//-------------------------------------------------------------------------
{
//加载外部模块,该模块引用内部embedded模块
auto py_module=py::module_::import("py_module");
auto locals=py::dict("fmt"_a="{}+{}={}",**py_module.attr("__dict__"));
assert(locals["a"].cast<int>()==1);
assert(locals["b"].cast<int>()==2);
py::exec(R"(
c=a+b
message=fmt.format(a,b,c)
)",py::globals(),locals);
assert(locals["c"].cast<int>()==3);
py::print(locals["message"]);
}
//------------------------------------------------------------------------
return(0);
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4)
project(embed)
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
add_executable(embed embed.cpp)
target_link_libraries(embed PRIVATE pybind11::embed)
使用pybind11在C++中嵌入Python并传递字典参数
pybind11是一个轻量级的C++库,用于将C++代码暴露给Python,同时也支持在C++中嵌入Python解释器并调用Python代码。下面我将介绍如何在C++中嵌入Python解释器、调用Python脚本以及传递字典参数。
基本设置
首先,确保你已经安装了pybind11和Python开发环境。
CMakeLists.txt 示例
cmake_minimum_required(VERSION 3.4)
project(embed_python)
find_package(Python REQUIRED COMPONENTS Interpreter Development)
find_package(pybind11 REQUIRED)
add_executable(embed_python main.cpp)
target_link_libraries(embed_python PRIVATE pybind11::embed Python::Python)
基本嵌入和字典传递示例
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <iostream>
namespace py = pybind11;
int main() {
// 启动Python解释器
py::scoped_interpreter guard{};
// 创建一个Python字典
py::dict my_dict;
my_dict["name"] = "Alice";
my_dict["age"] = 30;
my_dict["scores"] = py::list{90, 85, 95};
// 方法1:直接在C++中执行Python代码
py::exec(R"(
def print_dict(d):
print("Python received dict:", d)
print("Name:", d["name"])
print("Age:", d["age"])
print("Scores:", d["scores"])
# 修改字典
d["modified"] = True
return {"response": "success", "original_keys": list(d.keys())}
)");
// 获取Python函数
auto print_dict = py::globals()["print_dict"];
// 调用函数并传递字典
auto result = print_dict(my_dict);
// 处理返回的字典
std::cout << "C++ received response: " << result["response"].cast<std::string>() << std::endl;
// 检查原始字典是否被修改
std::cout << "Original dict was modified: " << my_dict.contains("modified") << std::endl;
// 方法2:从文件导入Python模块
try {
// 添加当前目录到Python路径
py::module_ sys = py::module_::import("sys");
sys.attr("path").attr("append")(".");
// 导入Python模块
py::module_ mymodule = py::module_::import("mymodule");
// 调用模块中的函数并传递字典
auto result2 = mymodule.attr("process_data")(my_dict);
std::cout << "Module response: " << result2.cast<std::string>() << std::endl;
} catch (const py::error_already_set& e) {
std::cerr << "Python error: " << e.what() << std::endl;
}
return 0;
}
对应的Python模块 (mymodule.py)
def process_data(data_dict):
print("Processing data in Python module...")
print("Received dictionary:", data_dict)
# 添加新键值
data_dict["processed"] = True
# 计算平均分
scores = data_dict["scores"]
average = sum(scores) / len(scores)
data_dict["average_score"] = average
return f"Processed data for {data_dict['name']}, average score: {average:.2f}"
字典参数传递的关键点
-
创建字典:
py::dict my_dict; my_dict["key"] = value;
-
从C++类型转换:
value
可以是基本类型(int, float, string等)或其他Python兼容类型- 也可以嵌套其他容器:
my_dict["nested"] = py::dict{ {"a", 1}, {"b", 2} };
-
访问返回的字典:
auto value = result["key"].cast<Type>();
-
类型转换:
- 使用
.cast<T>()
将Python对象转换为C++类型 - 使用
py::cast()
将C++对象转换为Python对象
- 使用
错误处理
建议使用try-catch块捕获Python异常:
try {
// Python调用代码
} catch (const py::error_already_set& e) {
std::cerr << "Python error: " << e.what() << std::endl;
// 可以调用 e.matches(PyExc_TypeError) 检查特定异常类型
}
性能考虑
- 频繁的C+±Python边界 crossing 会有性能开销
- 对于大量数据,考虑:
- 使用NumPy数组(通过pybind11的
py::array_t
) - 尽量减少跨语言调用次数
- 在可能的情况下批量处理数据
- 使用NumPy数组(通过pybind11的
通过以上方法,你可以在C++和Python之间高效地传递复杂的数据结构如字典,实现两种语言的互操作。
使用 pybind11 在 C++ 中嵌入 Python 并调用脚本函数
pybind11 不仅可以用于将 C++ 代码暴露给 Python,还可以在 C++ 中嵌入 Python 解释器并调用 Python 脚本。以下是完整的指南:
1. 基本设置
首先确保你已经安装了 pybind11 和 Python 开发环境。
CMake 配置示例
find_package(Python REQUIRED COMPONENTS Interpreter Development)
find_package(pybind11 REQUIRED)
add_executable(embed_python main.cpp)
target_link_libraries(embed_python PRIVATE pybind11::embed Python::Python)
2. 基本嵌入和脚本执行
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
// 启动 Python 解释器
py::scoped_interpreter guard{};
// 执行简单的 Python 代码
py::exec("print('Hello from Python!')");
// 执行 Python 脚本文件
try {
py::module_ script = py::module_::import("script");
} catch (const std::exception &e) {
std::cerr << "Failed to import script: " << e.what() << "\n";
return 1;
}
return 0;
}
3. 调用 Python 函数并传递参数
假设有一个 script.py
文件:
def add(a, b):
return a + b
def greet(name):
return f"Hello, {name}!"
def process_list(lst):
return [x * 2 for x in lst]
C++ 调用方式:
#include <pybind11/embed.h>
#include <iostream>
#include <vector>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
try {
// 导入 Python 模块
py::module_ script = py::module_::import("script");
// 调用简单函数 (add)
int result = script.attr("add")(5, 7).cast<int>();
std::cout << "5 + 7 = " << result << "\n";
// 调用字符串处理函数 (greet)
std::string greeting = script.attr("greet")("World").cast<std::string>();
std::cout << greeting << "\n";
// 传递和接收复杂类型 (list)
std::vector<int> input = {1, 2, 3, 4};
py::list py_list = py::cast(input);
py::list output = script.attr("process_list")(py_list).cast<py::list>();
std::cout << "Processed list: ";
for (auto item : output) {
std::cout << item.cast<int>() << " ";
}
std::cout << "\n";
// 使用字典传递参数
py::dict kwargs;
kwargs["a"] = 10;
kwargs["b"] = 20;
result = script.attr("add")(**kwargs).cast<int>();
std::cout << "10 + 20 = " << result << "\n";
} catch (const py::error_already_set &e) {
std::cerr << "Python error: " << e.what() << "\n";
return 1;
} catch (const std::exception &e) {
std::cerr << "C++ error: " << e.what() << "\n";
return 1;
}
return 0;
}
4. 处理 Python 返回值
pybind11 可以自动转换许多 Python 和 C++ 类型:
// 获取 Python 函数返回的多种类型
py::object result = script.attr("some_function")(...);
if (py::isinstance<py::int_>(result)) {
int int_result = result.cast<int>();
}
else if (py::isinstance<py::float_>(result)) {
double double_result = result.cast<double>();
}
else if (py::isinstance<py::str>(result)) {
std::string str_result = result.cast<std::string>();
}
else if (py::isinstance<py::list>(result)) {
std::vector<int> vec_result = result.cast<std::vector<int>>();
}
5. 设置 Python 路径
如果需要导入不在默认路径中的模块:
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
// 添加自定义路径到 Python 模块搜索路径
py::module_ sys = py::module_::import("sys");
sys.attr("path").attr("append")("/path/to/your/modules");
// 现在可以导入自定义路径中的模块
py::module_ custom_mod = py::module_::import("custom_module");
return 0;
}
6. 错误处理
良好的错误处理对于嵌入式 Python 很重要:
try {
py::module_ script = py::module_::import("nonexistent_module");
} catch (py::error_already_set &e) {
std::cerr << "Python error:\n";
// 获取 Python 异常信息
py::module_ traceback = py::module_::import("traceback");
py::object format_exc = traceback.attr("format_exc");
std::string error_msg = format_exc().cast<std::string>();
std::cerr << error_msg << "\n";
// 或者直接使用 e.what()
std::cerr << "Simplified error: " << e.what() << "\n";
// 清除 Python 错误状态
e.restore();
PyErr_Clear();
}
7. 性能考虑
对于频繁调用的 Python 函数,考虑:
- 尽量减少 C++ 和 Python 之间的边界跨越
- 批量处理数据而不是单个处理
- 对于性能关键部分,考虑用 C++ 实现或用 pybind11 暴露给 Python
通过以上方法,你可以在 C++ 中灵活地嵌入 Python 解释器,调用 Python 脚本和函数,并在两种语言之间传递各种类型的参数。
使用 pybind11 在 C/C++ 中嵌入 Python
pybind11 是一个轻量级的 C++ 库,用于将 C++ 代码暴露给 Python,同时也可以用来在 C++ 中嵌入 Python 解释器并与之交互。下面我将详细介绍如何使用 pybind11 在 C/C++ 中嵌入 Python。
基本设置
首先确保你已经安装了 pybind11 和 Python 开发环境。
安装 pybind11
pip install pybind11
或者从源码安装:
git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build && cd build
cmake ..
make install
嵌入 Python 解释器
1. 初始化 Python 解释器
#include <pybind11/embed.h> // 必须包含这个头文件
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{}; // 启动 Python 解释器
// 你的代码...
return 0;
} // Python 解释器在这里自动关闭
调用 Python 脚本
2. 执行 Python 脚本文件
try {
py::module_ script = py::module_::import("your_script_name");
// 注意: 不需要加 .py 后缀
// 确保脚本所在目录在 Python 路径中
} catch (py::error_already_set &e) {
std::cerr << "Failed to import script: " << e.what() << std::endl;
return 1;
}
或者直接执行 Python 代码字符串:
py::exec("print('Hello from Python!')");
参数传递和函数调用
3. 调用 Python 函数并传递参数
假设有一个 Python 脚本 example.py
:
def add(a, b):
return a + b
def greet(name):
return f"Hello, {name}!"
在 C++ 中调用这些函数:
// 导入模块
py::module_ example = py::module_::import("example");
// 调用 add 函数
int result = example.attr("add")(1, 2).cast<int>();
std::cout << "1 + 2 = " << result << std::endl;
// 调用 greet 函数
std::string greeting = example.attr("greet")("World").cast<std::string>();
std::cout << greeting << std::endl;
4. 传递复杂参数
你可以传递各种类型的参数,包括自定义 C++ 对象(如果它们已经用 pybind11 暴露给 Python):
// 传递列表
py::list my_list;
my_list.append(1);
my_list.append(2.0);
my_list.append("three");
example.attr("process_list")(my_list);
// 传递字典
py::dict my_dict;
my_dict["key1"] = "value1";
my_dict["key2"] = 42;
example.attr("process_dict")(my_dict);
获取脚本模块全局符号
5. 访问模块的全局变量和函数
# example.py
GLOBAL_VAR = 42
def get_global():
return GLOBAL_VAR
def set_global(value):
global GLOBAL_VAR
GLOBAL_VAR = value
在 C++ 中访问和修改这些全局变量:
// 获取全局变量
int global_var = example.attr("GLOBAL_VAR").cast<int>();
std::cout << "Global var: " << global_var << std::endl;
// 修改全局变量
example.attr("GLOBAL_VAR") = 100;
// 通过函数验证修改
int new_value = example.attr("get_global")().cast<int>();
std::cout << "New global var: " << new_value << std::endl;
// 通过函数修改
example.attr("set_global")(200);
完整示例
下面是一个完整的示例,展示了上述所有功能:
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
// 启动 Python 解释器
py::scoped_interpreter guard{};
try {
// 执行简单 Python 代码
py::exec("print('Hello from embedded Python!')");
// 导入 Python 模块
py::module_ example = py::module_::import("example");
// 调用函数
int sum = example.attr("add")(3, 4).cast<int>();
std::cout << "3 + 4 = " << sum << std::endl;
// 访问全局变量
int global_var = example.attr("GLOBAL_VAR").cast<int>();
std::cout << "Initial global var: " << global_var << std::endl;
// 修改全局变量
example.attr("GLOBAL_VAR") = 100;
std::cout << "Modified global var: "
<< example.attr("get_global")().cast<int>() << std::endl;
// 传递复杂类型
py::list my_list;
my_list.append("item1");
my_list.append(2);
my_list.append(3.14);
example.attr("process_list")(my_list);
} catch (py::error_already_set &e) {
std::cerr << "Python error: " << e.what() << std::endl;
return 1;
}
return 0;
}
CMake 配置
为了编译这个项目,你需要一个合适的 CMakeLists.txt 文件:
cmake_minimum_required(VERSION 3.4...3.18)
project(embed_python)
find_package(Python REQUIRED COMPONENTS Development)
find_package(pybind11 REQUIRED)
add_executable(embed_python src/main.cpp)
target_link_libraries(embed_python PRIVATE pybind11::embed Python::Python)
注意事项
-
Python 路径:确保你的 Python 脚本所在目录在 Python 路径中。你可以通过以下方式添加路径:
py::module_ sys = py::module_::import("sys"); sys.attr("path").attr("append")("/path/to/your/scripts");
-
GIL(全局解释器锁):在多线程环境中,你需要管理 GIL。pybind11 提供了
py::gil_scoped_acquire
和py::gil_scoped_release
来帮助管理。 -
错误处理:始终准备好捕获
py::error_already_set
异常,因为 Python 操作可能会失败。 -
内存管理:Python 对象在 C++ 中使用 pybind11 的智能指针管理,通常不需要手动处理。
通过以上方法,你可以轻松地在 C/C++ 中嵌入 Python 解释器,调用 Python 脚本,传递参数,并访问模块的全局符号。
使用 pybind11 在 C/C++ 中嵌入 Python
pybind11 是一个轻量级的头文件库,用于在 C++ 和 Python 之间创建绑定。它不仅可以用于创建 Python 扩展模块,还可以用于在 C++ 中嵌入 Python 解释器。
一、在 C/C++ 中嵌入 Python
基本步骤
-
包含必要头文件:
#include <pybind11/embed.h> namespace py = pybind11;
-
初始化 Python 解释器:
int main() { py::scoped_interpreter guard{}; // 启动 Python 解释器 // 你的代码... return 0; } // 解释器在 guard 析构时关闭
-
执行 Python 代码:
// 执行简单 Python 代码 py::exec("print('Hello from Python!')"); // 导入模块 py::module sys = py::module::import("sys"); py::print(sys.attr("path"));
完整示例
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
// 启动 Python 解释器
py::scoped_interpreter guard{};
// 执行 Python 代码
py::exec(R"(
print("Hello from Python embedded in C++!")
def greet(name):
return f"Hello, {name}!"
)");
// 调用 Python 函数
auto greet = py::globals()["greet"];
auto result = greet("World").cast<std::string>();
std::cout << result << std::endl;
return 0;
}
二、在 C++ 中创建 Python 模块
基本步骤
-
定义 C++ 类和函数:
class MyClass { public: MyClass(int value) : value(value) {} int getValue() const { return value; } void setValue(int v) { value = v; } std::string greet(const std::string &name) { return "Hello, " + name + "! My value is " + std::to_string(value); } private: int value; };
-
使用 pybind11 创建绑定:
PYBIND11_MODULE(my_module, m) { m.doc() = "pybind11 example module"; // 绑定 MyClass py::class_<MyClass>(m, "MyClass") .def(py::init<int>()) .def("getValue", &MyClass::getValue) .def("setValue", &MyClass::setValue) .def("greet", &MyClass::greet); // 绑定自由函数 m.def("add", [](int a, int b) { return a + b; }); }
-
编译为 Python 模块:
使用 CMake 或直接编译为共享库:cmake_minimum_required(VERSION 3.4) project(my_module) find_package(pybind11 REQUIRED) pybind11_add_module(my_module my_module.cpp)
完整示例
my_module.cpp:
#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;
class MyClass {
public:
MyClass(int value) : value(value) {}
int getValue() const { return value; }
void setValue(int v) { value = v; }
std::string greet(const std::string &name) {
return "Hello, " + name + "! My value is " + std::to_string(value);
}
private:
int value;
};
PYBIND11_MODULE(my_module, m) {
m.doc() = "pybind11 example module";
py::class_<MyClass>(m, "MyClass")
.def(py::init<int>())
.def("getValue", &MyClass::getValue)
.def("setValue", &MyClass::setValue)
.def("greet", &MyClass::greet);
m.def("add", [](int a, int b) { return a + b; });
m.attr("version") = "1.0";
}
Python 中使用:
import my_module
obj = my_module.MyClass(42)
print(obj.greet("Python")) # 输出: Hello, Python! My value is 42
print(my_module.add(2, 3)) # 输出: 5
print(my_module.version) # 输出: 1.0
三、高级用法
1. 在嵌入式 Python 中使用 C++ 模块
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
try {
// 导入我们创建的模块
auto my_module = py::module::import("my_module");
// 创建 MyClass 实例
auto my_class = my_module.attr("MyClass")(10);
// 调用方法
auto result = my_class.attr("greet")("C++").cast<std::string>();
std::cout << result << std::endl;
} catch (const py::error_already_set &e) {
std::cerr << "Python error: " << e.what() << std::endl;
}
return 0;
}
2. 处理 Python 异常
try {
py::eval_file("script.py");
} catch (py::error_already_set &e) {
std::cerr << "Python error:\n" << e.what() << std::endl;
e.restore(); // 恢复 Python 错误状态
PyErr_Print(); // 打印 Python 错误信息
}
3. 转换 C++ 和 Python 类型
// C++ 到 Python
py::list py_list;
py_list.append(py::cast(1));
py_list.append(py::cast(2.0));
py_list.append(py::cast("three"));
// Python 到 C++
auto cpp_list = py_list.cast<std::vector<py::object>>();
四、构建系统配置
推荐使用 CMake 构建项目:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.4)
project(embed_python)
find_package(pybind11 REQUIRED)
# 嵌入式 Python 程序
add_executable(embed_example embed_example.cpp)
target_link_libraries(embed_example PRIVATE pybind11::embed)
# Python 模块
pybind11_add_module(my_module my_module.cpp)
五、注意事项
- Python 解释器生命周期:确保
scoped_interpreter
对象在需要 Python 功能时一直存在 - GIL 锁:在多线程环境中操作 Python 对象时需要获取 GIL
py::gil_scoped_acquire acquire; // 获取 GIL // Python 操作... // GIL 在 acquire 析构时释放
- 内存管理:注意 Python 和 C++ 之间的对象生命周期管理
- 模块路径:确保 Python 能找到你的模块,可能需要设置
PYTHONPATH
通过以上方法,你可以在 C++ 中灵活地嵌入 Python 解释器,并创建供 Python 使用的 C++ 模块。
Pybind11 使用范式与模式
Pybind11 是一个轻量级的 C++ 库,用于将 C++ 代码暴露给 Python(作为扩展模块)或将 Python 嵌入到 C++ 应用程序中。以下是其主要使用范式和模式:
1. 作为 Python 扩展的创建工具
基本绑定模式
#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(example, m) {
m.def("add", &add, "A function which adds two numbers");
}
类绑定
class Pet {
public:
Pet(const std::string &name) : name(name) {}
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
private:
std::string name;
};
PYBIND11_MODULE(example, m) {
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
}
高级特性
- 操作符重载
- 动态属性
- 继承和多态
- 异常转换
- 智能指针支持
- STL 容器自动转换
2. 嵌入 Python 解释器
基本嵌入模式
#include <pybind11/embed.h>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{}; // 启动解释器
py::module sys = py::module::import("sys");
py::print(sys.attr("path")); // 访问 Python 属性
py::exec("print('Hello from Python!')"); // 执行 Python 代码
}
混合编程模式
- C++ 调用 Python 函数:
py::module calc = py::module::import("calculator");
py::object result = calc.attr("add")(1, 2);
int n = result.cast<int>();
- Python 回调 C++:
m.def("call_python_func", [](py::object func) {
return func(1, 2);
});
3. 常见设计模式
工厂模式
class AbstractBase {
public:
virtual ~AbstractBase() = default;
virtual std::string name() const = 0;
};
class Derived : public AbstractBase {
public:
std::string name() const override { return "Derived"; }
};
PYBIND11_MODULE(example, m) {
py::class_<AbstractBase>(m, "AbstractBase");
py::class_<Derived, AbstractBase>(m, "Derived")
.def(py::init<>());
m.def("create_derived", []() {
return std::unique_ptr<AbstractBase>(new Derived());
});
}
单例模式
class Singleton {
public:
static Singleton& instance() {
static Singleton instance;
return instance;
}
// 删除复制构造函数和赋值操作符
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
void doSomething() { /*...*/ }
private:
Singleton() = default;
};
PYBIND11_MODULE(example, m) {
py::class_<Singleton>(m, "Singleton")
.def("doSomething", &Singleton::doSomething)
.def_static("instance", &Singleton::instance,
py::return_value_policy::reference);
}
4. 性能关键模式
缓冲区协议 (NumPy 互操作)
#include <pybind11/numpy.h>
void double_array(py::array_t<double> arr) {
py::buffer_info buf = arr.request();
double *ptr = static_cast<double *>(buf.ptr);
for (size_t i = 0; i < buf.size; i++) {
ptr[i] *= 2;
}
}
PYBIND11_MODULE(example, m) {
m.def("double_array", &double_array, "Double all elements in a NumPy array");
}
避免拷贝大对象
void process_large_data(const std::vector<double>& data) {
// 处理数据,避免不必要的拷贝
}
PYBIND11_MODULE(example, m) {
m.def("process_large_data", &process_large_data,
py::arg().noconvert());
}
5. 现代 C++ 特性集成
使用 lambda 表达式
m.def("lambda_example", []() {
return "Hello from lambda!";
});
移动语义支持
class Movable {
public:
Movable() = default;
Movable(Movable&&) = default;
// ...
};
PYBIND11_MODULE(example, m) {
py::class_<Movable>(m, "Movable")
.def(py::init<>())
.def("__repr__", [](const Movable&) { return "<Movable>"; });
}
Pybind11 通过这些模式提供了灵活的方式在 Python 和 C++ 之间架起桥梁,既能保持高性能又能提供 Pythonic 的接口。
Pybind11 介绍与使用指南
Pybind11 简介
Pybind11 是一个轻量级的 C++ 库,用于将 C++ 代码暴露给 Python(反之亦然)。它是 Boost.Python 的现代替代品,具有更简洁的 API 和更好的性能。
主要特点
- 头文件库:只需包含头文件,无需额外编译
- C++11 支持:充分利用现代 C++ 特性
- 低开销:调用开销接近于原生 Python 代码
- 类型安全:自动类型转换
- 跨平台:支持 Windows、Linux 和 macOS
- 与 NumPy 集成:支持 NumPy 数组的无缝转换
基本使用
安装
pip install pybind11
或者从源码安装:
git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build && cd build
cmake ..
make install
简单示例
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // 模块文档
m.def("add", &add, "A function that adds two numbers"); // 导出函数
}
编译命令(使用 CMake):
cmake_minimum_required(VERSION 3.4...3.18)
project(example)
add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)
导出 C++ 类
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Pet {
public:
Pet(const std::string &name) : name(name) {}
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
private:
std::string name;
};
PYBIND11_MODULE(example, m) {
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
}
使用 Pybind11 的开源项目
以下是一些知名开源项目中使用 Pybind11 的例子:
-
TensorFlow - Google 的机器学习框架
- 用于将 C++ 核心与 Python API 绑定
-
PyTorch - Facebook 的机器学习库
- 使用 Pybind11 作为其 C++ 后端与 Python 前端之间的桥梁
-
OpenMM - 分子动力学模拟工具包
- 使用 Pybind11 暴露其 C++ API 给 Python
-
xtensor - NumPy 风格的 C++ 数组库
- 使用 Pybind11 实现与 Python 的互操作性
-
MongoDB C++ Driver - MongoDB 的官方 C++ 驱动
- 使用 Pybind11 创建 Python 绑定
-
Dlib - 机器学习工具包
- 使用 Pybind11 提供 Python 接口
-
PCL (Point Cloud Library) - 点云处理库
- Python 绑定部分使用 Pybind11
-
Taichi - 高性能计算语言
- 使用 Pybind11 将核心引擎暴露给 Python
-
Magnum - 轻量级模块化 C++11/C++14 图形中间件
- 使用 Pybind11 提供 Python 绑定
-
pybullet - 物理模拟引擎
- 使用 Pybind11 将 Bullet 物理引擎暴露给 Python
高级特性
- NumPy 支持:通过
pybind11/numpy.h
实现高效数组交互 - 智能指针:支持
std::shared_ptr
和std::unique_ptr
- 函数重载:自动解析 C++ 重载函数
- Python 特性支持:可实现 Python 的特殊方法(如
__add__
,__iter__
等) - 异常处理:C++ 异常与 Python 异常的转换
- 回调函数:Python 函数作为 C++ 回调
Pybind11 因其简洁性和高性能,已成为 C++/Python 互操作的事实标准工具之一,特别适合科学计算、机器学习和高性能计算领域的项目。

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)