如何检查 PyObject 是不是为列表?

Posted

技术标签:

【中文标题】如何检查 PyObject 是不是为列表?【英文标题】:How do I check if a PyObject is a list?如何检查 PyObject 是否为列表? 【发布时间】:2019-07-18 08:39:46 【问题描述】:

我是 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

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

我通过以下方式绑定函数:

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

谢谢。

【问题讨论】:

使用boost::python,它比纯 C - python 桥更容易掌握,除非你只使用 C。 【参考方案1】:

错误

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_BadInternalCall 是 PyErr_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 类型以外的参数时。

【讨论】:

非常感谢,完美运行。但是我还是不明白,我的代码哪里出错了?在我的情况下,它不应该只返回零吗? @Klemens Kasseroller 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

以上是关于如何检查 PyObject 是不是为列表?的主要内容,如果未能解决你的问题,请参考以下文章

Python C Api 将 PyObject * 传输到 c 数组中

确定 PyObject* 是不是为 PyLongDoubleScalarObject (numpy)

如何检查数组是不是为列表? [复制]

检查 PyObjects C 类型

如何检查值是不是在列表中或列表是不是为空?

如何将 C++ 对象转换为 PyObject?