如何检查PyObject是否为列表?

Kle*_*ler 2 c++ python python-c-api

我是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);
}
Run Code Online (Sandbox Code Playgroud)

基本上,这是文档页面上介绍中的功能。它应该接收一个python列表并返回所有元素的总和。如果我通过一个列表,该函数工作正常,如果我通过其他内容,但是出现错误消息

SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function
Run Code Online (Sandbox Code Playgroud)

这种情况应该由if (n<0)语句处理,如果传递的对象不是列表,则n为-1。

我通过以下方式绑定功能:

static PyMethodDef example_module_methods[] = {
    { "sum_list", (PyCFunction)sum_elements, METH_O, nullptr},
    { nullptr, nullptr, 0, nullptr }
};
Run Code Online (Sandbox Code Playgroud)

谢谢。

Abd*_*P M 5

The error

SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function
Run Code Online (Sandbox Code Playgroud)

is actually occurs at

Py_ssize_t n = PyList_Size(o)
Run Code Online (Sandbox Code Playgroud)

Because PyList_Size has an extra check to see whether the object of list type, If not it will call PyErr_BadInternalCall API to raise the SystemError. See the implementation of PyList_Size in listobject.c

PyList_Size(PyObject *op)
{
    if (!PyList_Check(op)) {
        PyErr_BadInternalCall();
        return -1;
    }
    else
        return Py_SIZE(op);
}
Run Code Online (Sandbox Code Playgroud)

The PyErr_BadInternalCall a shorthand for PyErr_SetString(PyExc_SystemError, message), where message indicates that an internal operation (e.g. a Python/C API function) was invoked with an illegal argument.

You should use PyList_Check API to check whether the object is of list type . As per the doc it Return true if object is a list object or an instance of a subtype of the list type.

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);
}
Run Code Online (Sandbox Code Playgroud)

Once this extra check is added, the function call will raise

TypeError: The argument must be of list or sub type of list
Run Code Online (Sandbox Code Playgroud)

when the argument other than list type is supplied.