Python 如何从PyListObject中“弹出”元素?

Python 如何从PyListObject中“弹出”元素?,python,python-3.x,list,cpython,python-c-api,Python,Python 3.x,List,Cpython,Python C Api,假设我有一个PyListObject,我想附加一个PyObject,然后我可以使用PyList\u appendAPI,该API在中有说明。但是对于我的用例,我希望pop来自PyListObject的元素(即python层中的my_list.pop()) 但是列表对象C-API文档没有提到任何关于pop操作的内容 那么,是否有关于PyListPopAPI函数的可用文档?您必须自己发布它。以下是一个可能的实现(无需错误检查): PyObject*my\u pop\u from\u list(PyL

假设我有一个
PyListObject
,我想附加一个
PyObject
,然后我可以使用
PyList\u append
API,该API在中有说明。但是对于我的用例,我希望
pop
来自
PyListObject
的元素(即python层中的
my_list.pop()

但是列表对象C-API文档没有提到任何关于
pop
操作的内容


那么,是否有关于
PyListPop
API函数的可用文档?

您必须自己发布它。以下是一个可能的实现(无需错误检查):

PyObject*my\u pop\u from\u list(PyListObject*lst){
//TODO:检查lst是否为空
Py_SIZE(lst)-=1;//忘记最后一个元素
返回PyList_GET_项(lst,PyList_GET_SIZE(lst));//返回最后一个元素
}
只是一个宏来访问
lst->ob_size
,我们在执行
pop
时会减小它

此外,还使用了未进行错误检查的版本,即
PyList\u GET\u ITEM
PyList\u GET\u SIZE
,因为一旦建立(请参阅TODO注释),列表不是空的-不会出错

调用者接收到一个新的引用,尽管
PyList\u GET\u ITEM
返回一个借用的引用:按照上面代码中的方法减小列表的大小,使列表在不减少引用计数器的情况下“忘记”引用


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

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

鉴于
list.pop
已经存在并且是用C实现的,您只需查看CPython实现的功能:

静态PyObject*
列表(PyListObject*self,Py\u ssize\u t索引)
{
PyObject*v;
智力状态;
如果(Py_大小(自身)==0){
/*特殊情况最常见的故障原因*/
PyErr_SetString(PyExc_索引器,“从空列表中弹出”);
返回NULL;
}
如果(指数<0)
索引+=Py_大小(自身);
如果(索引<0 | |索引>=Py|u大小(自身)){
PyErr_SetString(PyExc_索引器,“弹出索引超出范围”);
返回NULL;
}
v=自身->观察项目[索引];
如果(索引==Py_大小(自)-1){
状态=列表大小(自调整,自调整大小)-1;
如果(状态>=0)
返回v;/*并且v现在拥有列表中的引用*/
其他的
返回NULL;
}
Py_增量(v);
status=list\u ass\u slice(self,index,index+1,(PyObject*)NULL);
如果(状态<0){
Py_DECREF(v);
返回NULL;
}
返回v;
}

这包括许多C扩展无法(轻松)访问的函数,它还可以处理从特定索引(甚至是负索引)弹出的操作。就个人而言,我甚至懒得重新实现它,只需使用以下命令调用
pop
方法:

PyObject*
列表(PyObject*lst){
返回PyObject_CallMethod(lst,“pop”,“n”,Py_SIZE(lst)-1);
}
它可能比重新实现慢一点,但应该“更安全”——不能意外地弄乱列表对象的不变量(例如调整大小条件)

另一个实现出现在

static CYTHON\u INLINE PyObject*\u Pyx\u PyList\u Pop(PyObject*L){
/*检查两个尺寸是否为正,无需重新分配收缩*/
如果(可能(PyList_GET_SIZE(L)>((PyListObject*)L)->分配>>1))){
Py_尺寸(L)-=1;
返回PyList_GET_项目(L,PyList_GET_大小(L));
}
返回调用未绑定方法(PyList类型,“pop”,L);
}

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

请注意,此实现不会像实际的
list.pop
实现那样调整列表内部数组的大小。这可能会导致需要的内存超过需要的内存。