文章目录

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}"

字典参数传递的关键点

  1. 创建字典:

    py::dict my_dict;
    my_dict["key"] = value;
    
  2. 从C++类型转换:

    • value可以是基本类型(int, float, string等)或其他Python兼容类型
    • 也可以嵌套其他容器:
      my_dict["nested"] = py::dict{ {"a", 1}, {"b", 2} };
      
  3. 访问返回的字典:

    auto value = result["key"].cast<Type>();
    
  4. 类型转换:

    • 使用.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) 检查特定异常类型
}

性能考虑

  1. 频繁的C+±Python边界 crossing 会有性能开销
  2. 对于大量数据,考虑:
    • 使用NumPy数组(通过pybind11的py::array_t)
    • 尽量减少跨语言调用次数
    • 在可能的情况下批量处理数据

通过以上方法,你可以在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 函数,考虑:

  1. 尽量减少 C++ 和 Python 之间的边界跨越
  2. 批量处理数据而不是单个处理
  3. 对于性能关键部分,考虑用 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)

注意事项

  1. Python 路径:确保你的 Python 脚本所在目录在 Python 路径中。你可以通过以下方式添加路径:

    py::module_ sys = py::module_::import("sys");
    sys.attr("path").attr("append")("/path/to/your/scripts");
    
  2. GIL(全局解释器锁):在多线程环境中,你需要管理 GIL。pybind11 提供了 py::gil_scoped_acquirepy::gil_scoped_release 来帮助管理。

  3. 错误处理:始终准备好捕获 py::error_already_set 异常,因为 Python 操作可能会失败。

  4. 内存管理:Python 对象在 C++ 中使用 pybind11 的智能指针管理,通常不需要手动处理。

通过以上方法,你可以轻松地在 C/C++ 中嵌入 Python 解释器,调用 Python 脚本,传递参数,并访问模块的全局符号。


使用 pybind11 在 C/C++ 中嵌入 Python

pybind11 是一个轻量级的头文件库,用于在 C++ 和 Python 之间创建绑定。它不仅可以用于创建 Python 扩展模块,还可以用于在 C++ 中嵌入 Python 解释器。

一、在 C/C++ 中嵌入 Python

基本步骤

  1. 包含必要头文件

    #include <pybind11/embed.h>
    namespace py = pybind11;
    
  2. 初始化 Python 解释器

    int main() {
        py::scoped_interpreter guard{}; // 启动 Python 解释器
        
        // 你的代码...
        
        return 0;
    } // 解释器在 guard 析构时关闭
    
  3. 执行 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 模块

基本步骤

  1. 定义 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;
    };
    
  2. 使用 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; });
    }
    
  3. 编译为 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)

五、注意事项

  1. Python 解释器生命周期:确保 scoped_interpreter 对象在需要 Python 功能时一直存在
  2. GIL 锁:在多线程环境中操作 Python 对象时需要获取 GIL
    py::gil_scoped_acquire acquire;  // 获取 GIL
    // Python 操作...
    // GIL 在 acquire 析构时释放
    
  3. 内存管理:注意 Python 和 C++ 之间的对象生命周期管理
  4. 模块路径:确保 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 代码
}

混合编程模式

  1. C++ 调用 Python 函数:
py::module calc = py::module::import("calculator");
py::object result = calc.attr("add")(1, 2);
int n = result.cast<int>();
  1. 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 的例子:

  1. TensorFlow - Google 的机器学习框架

    • 用于将 C++ 核心与 Python API 绑定
  2. PyTorch - Facebook 的机器学习库

    • 使用 Pybind11 作为其 C++ 后端与 Python 前端之间的桥梁
  3. OpenMM - 分子动力学模拟工具包

    • 使用 Pybind11 暴露其 C++ API 给 Python
  4. xtensor - NumPy 风格的 C++ 数组库

    • 使用 Pybind11 实现与 Python 的互操作性
  5. MongoDB C++ Driver - MongoDB 的官方 C++ 驱动

    • 使用 Pybind11 创建 Python 绑定
  6. Dlib - 机器学习工具包

    • 使用 Pybind11 提供 Python 接口
  7. PCL (Point Cloud Library) - 点云处理库

    • Python 绑定部分使用 Pybind11
  8. Taichi - 高性能计算语言

    • 使用 Pybind11 将核心引擎暴露给 Python
  9. Magnum - 轻量级模块化 C++11/C++14 图形中间件

    • 使用 Pybind11 提供 Python 绑定
  10. pybullet - 物理模拟引擎

    • 使用 Pybind11 将 Bullet 物理引擎暴露给 Python

高级特性

  1. NumPy 支持:通过 pybind11/numpy.h 实现高效数组交互
  2. 智能指针:支持 std::shared_ptrstd::unique_ptr
  3. 函数重载:自动解析 C++ 重载函数
  4. Python 特性支持:可实现 Python 的特殊方法(如 __add__, __iter__ 等)
  5. 异常处理:C++ 异常与 Python 异常的转换
  6. 回调函数:Python 函数作为 C++ 回调

Pybind11 因其简洁性和高性能,已成为 C++/Python 互操作的事实标准工具之一,特别适合科学计算、机器学习和高性能计算领域的项目。

Logo

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

更多推荐