如何从PyListObject“弹出”元素?

2024-10-03 23:23:16 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个PyListObject,我想附加一个PyObject,然后我可以使用PyList_AppendAPI,它记录在^{}中。但是对于我的用例,我想要pop一个来自PyListObject(即python层中的my_list.pop())的元素。在

但是List Objects C-API文档没有提到pop操作的任何内容。在

那么有没有关于PyListPop.API函数的文档?。在


Tags: 文档api元素内容objectsmy记录用例
2条回答

不,不能通过PyListObject上的C-API直接使用list.pop方法

假设list.pop已经存在并且是用C实现的,那么您可以简单地查找CPython实现的功能:

static PyObject *
list_pop_impl(PyListObject *self, Py_ssize_t index)
{
    PyObject *v;
    int status;

    if (Py_SIZE(self) == 0) {
        /* Special-case most common failure cause */
        PyErr_SetString(PyExc_IndexError, "pop from empty list");
        return NULL;
    }
    if (index < 0)
        index += Py_SIZE(self);
    if (index < 0 || index >= Py_SIZE(self)) {
        PyErr_SetString(PyExc_IndexError, "pop index out of range");
        return NULL;
    }
    v = self->ob_item[index];
    if (index == Py_SIZE(self) - 1) {
        status = list_resize(self, Py_SIZE(self) - 1);
        if (status >= 0)
            return v; /* and v now owns the reference the list had */
        else
            return NULL;
    }
    Py_INCREF(v);
    status = list_ass_slice(self, index, index+1, (PyObject *)NULL);
    if (status < 0) {
        Py_DECREF(v);
        return NULL;
    }
    return v;
}

Source for CPython 3.7.2

这包括许多C扩展不容易访问的函数,它还处理从特定索引(甚至是负索引)中弹出的函数。就我个人而言,我甚至不想重新实现它,只需使用^{}调用pop方法:

^{pr2}$

它可能比重新实现慢一点,但应该“更安全”——不能意外地弄乱列表对象的不变量(例如调整大小条件)。在

另一个实现存在于Cython

^{3}$

也可以根据您的用例进行调整。在

你得自己动手。以下是一个可能的实现(不进行错误检查):

PyObject *my_pop_from_list(PyListObject *lst){
    //TODO: check lst isn't empty
    Py_SIZE(lst) -= 1;                                 // forget last element 
    return PyList_GET_ITEM(lst, PyList_GET_SIZE(lst)); // return last element
}

^{}只是一个访问lst->ob_size的宏,我们在执行pop时减少它。在

此外,还使用没有错误检查的版本,即PyList_GET_ITEMPyList_GET_SIZE,因为一旦它被建立(参见TODO注释),列表就不是空的-不会有任何问题。在

调用者接收到一个新的引用,尽管PyList_GET_ITEM返回一个借用的引用:按照我们在上面代码中所做的那样减小列表的大小,使list“忘记”引用而不减少引用计数器。在

正如@MSeifert所指出的,这个版本不会改变底层数组的大小,就像list.pop()所做的那样(如果在pop之后只使用了底层数组的一半或更少)。这可以看作是上述实现的“特性”——用内存交换速度。在

相关问题 更多 >