通过pybind11在C++端读取Python端的嵌套字典数据
下面是一个完整的示例程序,展示如何在C++端通过pybind11读取Python端传递的嵌套字典数据。
C++端代码 (pybind11_module.cpp)
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <iostream>
#include <string>
#include <unordered_map>
#include <variant>
namespace py = pybind11;
// 定义可以存储多种类型的variant
using ValueType = std::variant<
int,
float,
double,
std::string,
std::unordered_map<std::string, std::variant<int, float, double, std::string>>
>;
// 递归打印嵌套字典的函数
void print_nested_dict(const std::unordered_map<std::string, ValueType>& dict, const std::string& indent = "") {
for (const auto& pair : dict) {
std::cout << indent << pair.first << ": ";
if (std::holds_alternative<int>(pair.second)) {
std::cout << std::get<int>(pair.second) << " (int)" << std::endl;
} else if (std::holds_alternative<float>(pair.second)) {
std::cout << std::get<float>(pair.second) << " (float)" << std::endl;
} else if (std::holds_alternative<double>(pair.second)) {
std::cout << std::get<double>(pair.second) << " (double)" << std::endl;
} else if (std::holds_alternative<std::string>(pair.second)) {
std::cout << "\"" << std::get<std::string>(pair.second) << "\" (string)" << std::endl;
} else if (std::holds_alternative<std::unordered_map<std::string, ValueType>>(pair.second)) {
std::cout << "{" << std::endl;
print_nested_dict(std::get<std::unordered_map<std::string, ValueType>>(pair.second), indent + " ");
std::cout << indent << "}" << std::endl;
}
}
}
// 处理Python字典的函数
void process_dict(py::dict py_dict) {
std::unordered_map<std::string, ValueType> cpp_dict;
// 将Python字典转换为C++字典
for (auto item : py_dict) {
std::string key = py::cast<std::string>(item.first);
py::handle value = item.second;
if (py::isinstance<py::int_>(value)) {
cpp_dict[key] = py::cast<int>(value);
} else if (py::isinstance<py::float_>(value)) {
cpp_dict[key] = py::cast<double>(value);
} else if (py::isinstance<py::str>(value)) {
cpp_dict[key] = py::cast<std::string>(value);
} else if (py::isinstance<py::dict>(value)) {
// 递归处理嵌套字典
py::dict nested_py_dict = py::cast<py::dict>(value);
std::unordered_map<std::string, ValueType> nested_cpp_dict;
for (auto nested_item : nested_py_dict) {
std::string nested_key = py::cast<std::string>(nested_item.first);
py::handle nested_value = nested_item.second;
if (py::isinstance<py::int_>(nested_value)) {
nested_cpp_dict[nested_key] = py::cast<int>(nested_value);
} else if (py::isinstance<py::float_>(nested_value)) {
nested_cpp_dict[nested_key] = py::cast<double>(nested_value);
} else if (py::isinstance<py::str>(nested_value)) {
nested_cpp_dict[nested_key] = py::cast<std::string>(nested_value);
} else {
throw std::runtime_error("Unsupported nested value type");
}
}
cpp_dict[key] = nested_cpp_dict;
} else {
throw std::runtime_error("Unsupported value type");
}
}
// 打印处理后的字典
std::cout << "Processed dictionary in C++:" << std::endl;
print_nested_dict(cpp_dict);
}
PYBIND11_MODULE(nested_dict_example, m) {
m.doc() = "Example of processing nested Python dictionaries in C++";
m.def("process_dict", &process_dict, "Process a nested Python dictionary in C++");
}
Python端测试代码 (test.py)
import nested_dict_example
# 创建一个嵌套字典
nested_dict = {
"name": "John Doe",
"age": 30,
"scores": {
"math": 95.5,
"english": 88,
"history": 92.3
},
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"is_student": False,
"height": 175.2
}
# 调用C++函数处理字典
nested_dict_example.process_dict(nested_dict)
CMakeLists.txt (构建配置)
cmake_minimum_required(VERSION 3.4...3.18)
project(nested_dict_example)
# 查找Python和pybind11
find_package(Python REQUIRED COMPONENTS Development)
find_package(pybind11 REQUIRED)
# 创建Python模块
pybind11_add_module(nested_dict_example pybind11_module.cpp)
# 设置C++标准
set_target_properties(nested_dict_example PROPERTIES CXX_STANDARD 17)
编译和运行步骤
- 创建构建目录并编译:
mkdir build
cd build
cmake ..
make
- 运行Python测试脚本:
python test.py
预期输出
Processed dictionary in C++:
name: "John Doe" (string)
age: 30 (int)
height: 175.2 (double)
is_student: 0 (int)
scores: {
math: 95.5 (double)
english: 88 (int)
history: 92.3 (double)
}
address: {
street: "123 Main St" (string)
city: "New York" (string)
zip: "10001" (string)
}
代码说明
类型处理:
- 使用
std::variant
处理多种可能的字典值类型 - 支持基本类型(int, float, string)和嵌套字典
- 使用
递归处理:
- 递归地将Python嵌套字典转换为C++的嵌套
unordered_map
- 递归地打印嵌套字典结构
- 递归地将Python嵌套字典转换为C++的嵌套
类型检查:
- 使用
py::isinstance
检查Python对象的类型 - 对不支持的类型抛出异常
- 使用
构建系统:
- 使用CMake和pybind11构建Python扩展模块
这个示例展示了如何完整地在C++端处理Python传递的嵌套字典数据,包括类型转换、递归处理和错误检查。