在C++中使用Python/C API调用Python代码中类中的方法,并传递C++定义的数据结构,然后解析复杂的字典值,这个过程涉及多个步骤。以下是一个完整的流程和代码示例。
示例背景
假设我们有一个Python类Person,它包含一个方法get_info,该方法返回一个包含个人信息的复杂字典。C++代码需要将自定义的数据结构传递给这个Python方法,并解析返回的复杂字典。
Python代码 (example.py)
# example.py
class Person:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def get_info(self):
return {
"name": self.name,
"age": self.age,
"address": {
"street": self.address["street"],
"city": self.address["city"],
"zip": self.address["zip"]
},
"hobbies": ["reading", "cycling"],
"is_student": self.age < 25
}
C++代码示例
在C++中调用Python类的方法,并解析其返回的复杂字典:
#include <Python.h>
#include <iostream>
#include <string>
#include <map>
// C++定义的地址结构
struct Address {
std::string street;
std::string city;
std::string zip;
};
// 函数声明
void print_dict(PyObject* pDict);
void print_list(PyObject* pList);
int main() {
// 1. 初始化Python解释器
Py_Initialize();
// 2. 导入Python模块
PyObject *pName = PyUnicode_DecodeFSDefault("example"); // example.py的模块名
PyObject *pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != nullptr) {
// 3. 获取类对象
PyObject *pClass = PyObject_GetAttrString(pModule, "Person");
if (pClass && PyCallable_Check(pClass)) {
// 4. 创建类的实例
Address address = {"123 Maple Street", "Springfield", "12345"};
// 创建Python的字典对象表示C++的地址结构
PyObject *pAddress = PyDict_New();
PyDict_SetItemString(pAddress, "street", PyUnicode_FromString(address.street.c_str()));
PyDict_SetItemString(pAddress, "city", PyUnicode_FromString(address.city.c_str()));
PyDict_SetItemString(pAddress, "zip", PyUnicode_FromString(address.zip.c_str()));
// 传递参数 (name, age, address)
PyObject *pArgs = PyTuple_Pack(3,
PyUnicode_FromString("Alice"),
PyLong_FromLong(22),
pAddress);
// 调用构造函数创建实例
PyObject *pInstance = PyObject_CallObject(pClass, pArgs);
Py_DECREF(pArgs);
if (pInstance != nullptr) {
// 5. 调用类中的方法get_info
PyObject *pResult = PyObject_CallMethod(pInstance, "get_info", nullptr);
if (pResult != nullptr && PyDict_Check(pResult)) {
// 6. 解析返回的复杂字典
print_dict(pResult);
Py_DECREF(pResult);
} else {
PyErr_Print(); // 打印错误信息
}
Py_DECREF(pInstance);
} else {
PyErr_Print(); // 打印错误信息
}
} else {
PyErr_Print(); // 打印错误信息
}
Py_XDECREF(pClass);
Py_DECREF(pModule);
} else {
PyErr_Print(); // 打印错误信息
}
// 7. 关闭Python解释器
Py_Finalize();
return 0;
}
// 解析和打印字典内容
void print_dict(PyObject* pDict) {
if (PyDict_Check(pDict)) {
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(pDict, &pos, &key, &value)) {
std::string keyStr = PyUnicode_AsUTF8(key);
if (PyUnicode_Check(value)) {
std::cout << keyStr << ": " << PyUnicode_AsUTF8(value) << std::endl;
} else if (PyLong_Check(value)) {
std::cout << keyStr << ": " << PyLong_AsLong(value) << std::endl;
} else if (PyFloat_Check(value)) {
std::cout << keyStr << ": " << PyFloat_AsDouble(value) << std::endl;
} else if (PyList_Check(value)) {
std::cout << keyStr << ": [";
print_list(value);
std::cout << "]" << std::endl;
} else if (PyDict_Check(value)) {
std::cout << keyStr << ": {" << std::endl;
print_dict(value);
std::cout << "}" << std::endl;
} else if (PyBool_Check(value)) {
std::cout << keyStr << ": " << (PyObject_IsTrue(value) ? "True" : "False") << std::endl;
} else {
std::cout << keyStr << ": (unhandled type)" << std::endl;
}
}
}
}
// 解析和打印列表内容
void print_list(PyObject* pList) {
if (PyList_Check(pList)) {
Py_ssize_t size = PyList_Size(pList);
for (Py_ssize_t i = 0; i < size; ++i) {
PyObject *item = PyList_GetItem(pList, i);
if (PyUnicode_Check(item)) {
std::cout << PyUnicode_AsUTF8(item);
} else if (PyLong_Check(item)) {
std::cout << PyLong_AsLong(item);
} else if (PyFloat_Check(item)) {
std::cout << PyFloat_AsDouble(item);
} else if (PyDict_Check(item)) {
std::cout << "{";
print_dict(item);
std::cout << "}";
} else if (PyBool_Check(item)) {
std::cout << (PyObject_IsTrue(item) ? "True" : "False");
} else {
std::cout << "(unhandled type)";
}
if (i < size - 1) {
std::cout << ", ";
}
}
}
}
代码解析
初始化和关闭Python解释器:
Py_Initialize() 启动Python解释器。
Py_Finalize() 关闭Python解释器。
导入Python模块:
PyUnicode_DecodeFSDefault 将模块名称从C字符串转换为Python字符串。
PyImport_Import 导入指定的Python模块 (example.py)。
创建类的实例:
PyObject_GetAttrString 获取Python模块中的类对象 (Person)。
使用 PyDict_New 和 PyDict_SetItemString 创建并填充表示C++结构的Python字典。
PyTuple_Pack 用于将C++字符串、整数、字典等转换为Python对象,并打包成一个元组,作为构造函数的参数。
PyObject_CallObject 调用Python类的构造函数并创建一个实例。
调用类方法并解析返回的复杂字典:
使用 PyObject_CallMethod 调用类中的方法 (get_info)。
print_dict 函数递归解析并打印字典的键值对。
处理不同的数据类型:
使用 PyUnicode_AsUTF8 解析Python字符串。
使用 PyLong_AsLong 解析长整型。
使用 PyFloat_AsDouble 解析浮点数。
PyObject_IsTrue 用于布尔值的判断。
对于嵌套的列表和字典,使用递归解析。
错误处理:
使用 PyErr_Print 输出详细的错误信息,有助于调试。
总结
通过这种方法,可以在C++中调用Python类中的方法,并传递自定义的C++数据结构。同时,也可以解析返回的复杂字典数据结构,处理包含字符串、整数、列表、嵌套字典等各种类型的值。