You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Python C API从C函数返回两个PyObject列表?

Python C API 返回多个列表的正确方法

为什么自定义结构体不可行

你的C函数声明返回PyObject*类型,但自定义结构体TupleLists是C原生值类型,无法直接转换为Python对象指针,这就是编译错误的核心原因。Python C API要求扩展函数必须返回合法的Python对象指针,自定义结构体不属于Python对象体系,因此不能用来返回值。

推荐方案:返回Python元组

Python中函数返回多个值的本质是返回一个元组,在C API里可以通过以下步骤实现:

  1. 生成需要返回的两个Python列表对象
  2. 创建一个包含2个元素的Python元组
  3. 将两个列表存入元组的对应位置
  4. 返回该元组对象

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 07:04:52