如何通过Python C API从C函数返回两个PyObject列表?
Python C API 返回多个列表的正确方法
为什么自定义结构体不可行
你的C函数声明返回PyObject*类型,但自定义结构体TupleLists是C原生值类型,无法直接转换为Python对象指针,这就是编译错误的核心原因。Python C API要求扩展函数必须返回合法的Python对象指针,自定义结构体不属于Python对象体系,因此不能用来返回值。
推荐方案:返回Python元组
Python中函数返回多个值的本质是返回一个元组,在C API里可以通过以下步骤实现:
- 生成需要返回的两个Python列表对象
- 创建一个包含2个元素的Python元组
- 将两个列表存入元组的对应位置
- 返回该元组对象
修改后的完整代码
static PyObject *PySolveProblem(PyObject *self, PyObject *args) { if(PyObject_Length(args) != 2) { PyErr_SetString(PyExc_TypeError, "Expected two arguments"); return NULL; } PyObject *arg1 = PyObject_GetItem(args, PyLong_FromLong(0)); PyObject *arg2 = PyObject_GetItem(args, PyLong_FromLong(1)); if(!PyUnicode_Check(arg1) || !PyUnicode_Check(arg2)) { PyErr_SetString(PyExc_TypeError, "Arguments must be strings"); return NULL; } const char *paramsStr = PyUnicode_AsUTF8(arg1); const char *problemStr = PyUnicode_AsUTF8(arg2); gbString params = gb_make_string(paramsStr); gbString problem = gb_make_string(problemStr); int tourSize = 0; int *tourPtr = NULL; int *finalHistory = NULL; int finalHistorySize = 0; int jmpRes; jmpRes = setjmp(ErrorJumpBuffer); if(jmpRes != 0) { gb_free_string(params); gb_free_string(problem); // 释放C侧分配的内存 if(tourPtr) free(tourPtr); if(finalHistory) free(finalHistory); return NULL; } ElkaiSolveProblem(params, problem, &tourSize, &tourPtr, &finalHistorySize, &finalHistory); if(PyErr_Occurred() != NULL) { if(tourPtr) free(tourPtr); if(finalHistory) free(finalHistory); gb_free_string(params); gb_free_string(problem); return NULL; } // 创建第一个列表:tour列表 PyObject *tourList = PyList_New(tourSize); for (int i = 0; i < tourSize; i++) { PyObject *tourElement = PyLong_FromLong((long)(tourPtr[i])); PyList_SetItem(tourList, i, tourElement); } // 创建第二个列表:history列表 PyObject *historyList = PyList_New(finalHistorySize); for (int i = 0; i < finalHistorySize; i++) { PyObject *historyElement = PyLong_FromLong((long)(finalHistory[i])); PyList_SetItem(historyList, i, historyElement); } // 创建结果元组 PyObject *resultTuple = PyTuple_New(2); PyTuple_SetItem(resultTuple, 0, tourList); PyTuple_SetItem(resultTuple, 1, historyList); // 释放C侧分配的内存 gb_free_string(params); gb_free_string(problem); if(tourPtr) free(tourPtr); if(finalHistory) free(finalHistory); return resultTuple; }
关键细节说明
PyTuple_New(2):创建一个包含2个元素的空元组PyTuple_SetItem:将列表对象存入元组,该函数会自动接管对象的引用计数,无需手动调用Py_DECREF- 内存泄漏防护:必须释放
ElkaiSolveProblem分配的tourPtr和finalHistory指针,以及gbString对象,避免内存泄漏 - 错误处理分支中也要同步释放已分配的内存,防止异常路径下的泄漏
内容的提问来源于stack exchange,提问作者Alisa Petrova
相关产品推荐
相关产品推荐

