使用 swig 将列表或数组数组从 python 传递到 C

问题描述

我已经搜索了一段时间,但即使问题很简单也找不到答案。 在 python 中,我声明了一个包含 N 个地方的列表,其元素是列表本身:

list_of_list = []
for i in range(N):
    list_of_list.append([])

然后我想将此列表列表传递给我的 C 扩展并填充它/阅读它。例如,在 C:

void * fill_list_of_list (PyObject *args){
    int ok;
    PyObject *list_of_list;
    int i,N;
    
    ok = ( PyArg_ParseTuple(args,"iO",&N,&list_of_list));
    
    for (i=0; i < N; i++){
       /*would like to set,for each sublist,its first element to zero*/
       PyList_SetItem( PyList_GetItem(list_of_list,i),PyFloat_FromDouble(0.) );
    }
 }

从python然后我想要以下代码

print(list_of_list)
fill_list_of_list((N,list_of_list))
print(list_of_list)

输出(例如 N = 3):

[[],[],[]]
[[0.],[0.],[0.]]

但我收到了分段错误

我做错了什么?请注意,问题确实出在列表列表上,因为以类似的方式我可以完美地处理浮动列表。与 numpy 浮点数数组和不同大小数组的 numpy 数组类似。

解决方法

PyList_SetItem 基本上替换列表中的一个项目,但您的列表是空的,所以没有任何东西可以替换。当您尝试这样做时,CPython API 应该设置一个异常。您可能想尝试改用 PyList_Append。我还强烈建议您对 CPython 函数进行错误检查。

不过,我不确定这是否是唯一的问题,因为您需要提供一个最小的、可重现的示例(您的代码似乎有一些错误,例如 {{ 旁边的未配对 ( 1}} 和未声明的变量 PyArg_ParseTuple)。我也不熟悉使用 SWIG,因此请将此视为部分答案,因为我可能错过了更具体的 SWIG 内容。

编辑
根据评论,这是一个使用 timestamps 的工作示例。请注意,我只使用 CPython API(没有 SWIG)。如果您想自己构建和运行此示例,还必须编辑 Python 模块文件的路径。

PyList_Append
/* main.cpp */
#include <iostream>
#include <cstdio>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

#define PYTHONFILE "Path\\To\\Python\\File\\test.py" // <-- Change this to the location  
                                                     //      of your Python module file

extern "C" {
    static PyObject *Foo_fillListOfLists(PyObject *,PyObject *);
}

static PyMethodDef FooMethods[] =
{
    {
        "fill_list_of_lists",Foo_fillListOfLists,METH_VARARGS,"Fills list of lists with zeros."
    },{ NULL,NULL,NULL }
};

static struct PyModuleDef FooModule =
{
    PyModuleDef_HEAD_INIT,"foo",-1,FooMethods
};

PyMODINIT_FUNC PyInit_Foo(void)
{
    return PyModule_Create(&FooModule);
}

void RunPython()
{
    FILE *fp = NULL;

    if (fopen_s(&fp,PYTHONFILE,"rb"))
        return;

    if (PyImport_AppendInittab("foo",PyInit_Foo) == -1) {
        std::cout << "ERROR: Could not extend built-in modules table\n";
        fclose(fp);
        return;
    }

    Py_Initialize();
    PyRun_SimpleFile(fp,PYTHONFILE);
    Py_FinalizeEx();

    fclose(fp);
}

int main(int argc,char *argv[])
{
    RunPython();
    return 0;
}

PyObject *Foo_fillListOfLists(PyObject *self,PyObject *args)
{
    PyObject *listOfLists;
    Py_ssize_t listLen;

    if (!PyArg_ParseTuple(args,"O",&listOfLists))
        return NULL;

    if (!PyList_CheckExact(listOfLists)) {
        PyErr_SetString(PyExc_RuntimeError,"Received non-list type object.");
        return NULL;
    }

    listLen = PyList_GET_SIZE(listOfLists);
    for (Py_ssize_t i = 0; i < listLen; ++i) {
        PyObject *listInList = PyList_GET_ITEM(listOfLists,i);

        if (!PyList_CheckExact(listInList)) {
            PyErr_SetString(PyExc_RuntimeError,"Non-list type found in list of lists.");
            return NULL;
        }

        // This is what you need to fix your error.
        if (PyList_Append(listInList,PyFloat_FromDouble(0)))
            return NULL;
    }

    return Py_None;
}

输出:

# test.py
import foo

list_of_lists = []
for i in range(3):
    list_of_lists.append([])

print(list_of_lists)
foo.fill_list_of_lists(list_of_lists)
print(list_of_lists)