如何在Python C API中正确调用PyIter_Next对一个List或Iterable进行迭代?

问题描述 投票:0回答:1

我一直在努力在Cpython C API中拥有一些在序列或可迭代的通用方法。下面的示例代码在编译时没有出错,但在运行以下代码时却失败了。

it ok
Segmentation fault: 11

我猜测是在以下阶段失败了: while ((item = PyIter_Next(it))) 行,但这几乎没有争议,因为等价的python是非常标准的。

x = [1,2,3]

it = iter(x)

for i in it:
    print(i)

很明显,我在C语言的等价物上做错了什么。如果有任何帮助或指导,我将不胜感激

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);
    Py_Initialize();

    // create list
    PyObject *list = PyList_New(2);
    PyObject *n1 = PyLong_FromLong(1);
    PyObject *n2 = PyLong_FromLong(2);
    PyList_Append(list, n1);
    PyList_Append(list, n2);

    PyObject *it = PyObject_GetIter(list);
    PyObject *item;
    int i = 0;

    if (it) {
        printf("it ok\n");
        while ((item = PyIter_Next(it))) {
            if (PyLong_Check(item)) {
                i++;
                long long_item = PyLong_AsLong(item);
                printf("%d long: %ld", i, long_item);
            } else if PyFloat_Check(item) {
                i++;
                float float_item = PyFloat_AsDouble(item);
                printf("%d float: %f", i, float_item);
            } else if PyUnicode_Check(item) {
                i++;
                const char *unicode_item = PyUnicode_AsUTF8(item);
                printf("%d unicode: %s", i, unicode_item);
            } else continue;

        Py_DECREF(item);
        Py_DECREF(it);
        }

    } else {
        if (PyErr_Occurred()) {
            PyErr_Print();
        }
        Py_DECREF(n1);
        Py_DECREF(n2);
        Py_DECREF(list);

        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        PyMem_RawFree(program);
        return 0;
    }
}

为了回应 @DavidW 对我的错误的有益观察,这里是更正后的代码,也许对其他人来说是个有用的例子。

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);
    Py_Initialize();

    // create list
    PyObject *list = PyList_New(0); // empty list
    PyObject *n1 = PyLong_FromLong(10);
    PyObject *n2 = PyLong_FromLong(20);
    PyList_Append(list, n1);
    PyList_Append(list, n2);

    PyObject *iter;
    PyObject *item;
    int i = 0;

    if ((iter = PyObject_GetIter(list)) == NULL) {
        return -1;
    }

    while ((item = PyIter_Next(iter)) != NULL) {
        if (PyLong_Check(item)) {
            i++;
            long long_item = PyLong_AsLong(item);
            printf("%d long: %ld\n", i, long_item);
        }

        if PyFloat_Check(item) {
            i++;
            float float_item = PyFloat_AsDouble(item);
            printf("%d float: %f\n", i, float_item);
        }

        if PyUnicode_Check(item) {
            i++;
            const char *unicode_item = PyUnicode_AsUTF8(item);
            printf("%d unicode: %s\n", i, unicode_item);
        }

        Py_DECREF(item);
    }

    Py_DECREF(iter);
    Py_DECREF(n1);
    Py_DECREF(n2);
    Py_DECREF(list);

    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}
python c python-c-api
1个回答
0
投票
PyObject *list = PyList_New(2);

这将创建一个长度为2的列表,内容未初始化(可能是 NULL 指针,但我不是100%确定)。)

PyList_Append(list, n1);
PyList_Append(list, n2);

这些附加到列表的末尾(即位置3和4)。

因此你有一个列表 <garbage>, <garbage>, n1, n2.

你要么更换 PyList_AppendPyList_SetItem或替换 PyList_New(2)PyList_New(0).


Py_DECREF(it);

应该在迭代完成后,而不是在循环中完成。

© www.soinside.com 2019 - 2024. All rights reserved.