如何检查PyObject是否为列表?

3

我对Python/C API还不熟悉,虽然我已经成功运行了一些基本函数,但我在这个问题上遇到了困难。

PyObject* sum_elements(PyObject*, PyObject *o) 
{
    Py_ssize_t n = PyList_Size(o);
    long total = 0;
    if (n < 0)
    {
        return PyLong_FromLong(total);
    }
    PyObject* item;
    for (int i = 0; i < n; i++) 
    {
        item = PyList_GetItem(o, i);
        if (!PyLong_Check(item)) continue;
        total += PyLong_AsLong(item);
    }
    return PyLong_FromLong(total);
}

基本上这是文档页介绍中的函数。它应该接收一个Python列表,并返回所有元素的总和。如果我传递一个列表,这个函数可以正常工作,但是如果我传递其他类型的值,就会出现错误信息。

SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function

如果传递的对象不是列表,n就会变成-1,因此应该使用if (n<0)语句来处理这种情况。

我是这样绑定函数的:

static PyMethodDef example_module_methods[] = {
    { "sum_list", (PyCFunction)sum_elements, METH_O, nullptr},
    { nullptr, nullptr, 0, nullptr }
};

感谢您的选择。

使用 boost::python,它比纯C-Python桥更容易掌握,除非你只能使用C。 - Radosław Cybulski
1个回答

6

错误

SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function

实际上发生在

Py_ssize_t n = PyList_Size(o)

因为PyList_Size有一个额外的检查,用于检查列表类型的对象。如果不是列表类型,则会调用PyErr_BadInternalCall API来引发SystemError。请参阅listobject.cPyList_Size的实现。
PyList_Size(PyObject *op)
{
    if (!PyList_Check(op)) {
        PyErr_BadInternalCall();
        return -1;
    }
    else
        return Py_SIZE(op);
}

PyErr_BadInternalCallPyErr_SetString(PyExc_SystemError, message)的简写,其中message表示使用非法参数调用了内部操作(例如Python/C API函数)。

您应该使用PyList_Check API来检查对象是否为list类型。根据文档,它返回true,如果对象是列表对象或列表类型的子类型的实例。

PyObject* sum_elements(PyObject*, PyObject *o) 
{    
    // Check if `o` is of `list` type, if not raise `TypeError`.
    if (!PyList_Check(o)) {
         PyErr_Format(PyExc_TypeError, "The argument must be of list or subtype of list");
         return NULL;
    }
    // The argument is list type, perform the remaining calculations.
    Py_ssize_t n = PyList_Size(o);
    long total = 0;
    if (n < 0)
    {
        return PyLong_FromLong(total);
    }
    PyObject* item;
    for (int i = 0; i < n; i++) 
    {
        item = PyList_GetItem(o, i);
        if (!PyLong_Check(item)) continue;
        total += PyLong_AsLong(item);
    }
    return PyLong_FromLong(total);
}

一旦添加了这个额外的检查,函数调用将会引发错误。
TypeError: The argument must be of list or sub type of list

当提供除list类型以外的参数时。

非常感谢,运行得非常完美。但我仍然不明白,我的代码出了什么错误?在我的情况下,它不应该只返回零吗? - kleka
@Klemens Kasseroller 在 Py_ssize_t n = PyList_Size(o); 处出现了 SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function 错误。如果您查看 PyList_Size 的实现,您会发现它检查列表类型的对象是否存在,如果不存在则调用 PyErr_BadInternalCall - Abdul Niyas P M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接