Python C API: PyList_Append 出错时做什么?

Python C API: What does PyList_Append do on error?

这在文档中没有记录。它只是说“returns 0 表示成功,-1 表示错误”。这是什么意思?

如果我们迭代某些数据结构(比如链接 list/array)并且我们在迭代时添加元素,突然 PyList_Append returns -1 - 发生什么了? did 的元素是否被添加,它们的引用计数是否减少,或者 Python 只是死掉并且取消分配/减少引用计数是我最不担心的?

如果到目前为止 添加的元素减少了它们的引用计数,那么我明白不需要做任何事情,因为在这种情况下,我的取消分配函数处理减少其他需要的引用计数(如果元素的引用计数达到 0)——但是如果 Python 只是举起手来说“你靠你自己”——我必须确保我管理引用在出错时立即计数(或遭受参考泄漏)。

那是哪一个?

我认为 PyList_Append 会出现两个错误:

  • MemoryError(如果要扩大分配给list的内存,分配不够)
  • SystemError 如果你传递给它的不是列表。你应该能够通过适当的 type-checks 避免这种情况,所以如果你的代码编写正确,它就永远不会发生。

在这两种情况下:

  • 列表的现有内容仍然是列表的一部分(并且它们的引用计数没有改变)。它们归列表所有,当列表被销毁时,它们的引用计数将减少 1,因此您不需要对它们做任何特殊操作,
  • 您尝试添加的元素未添加,其引用计数保持不变。作为调用者,您拥有 1 个对它的引用(在成功和失败的情况下),您可能想要 DECREF.

实际上 memory-errors 很难从大多数操作系统 over-allocate 内存中恢复,所以当您真正看到内存错误时,您通常 运行 已经用完了。


示例(未经测试)代码大致显示了您要执行的操作:

PyObject *make_list(int n) {
    PyObject* list = PyList_New(0);
    for (int i=0; i<n; ++i) {
        PyObject *toAdd = PyLong_FromLong(i);
        if (!toAdd) goto bad;
        int appendRes = PyList_Append(list, toAdd);
        Py_DECREF(toAdd);  // decref this whatever the result of append
        if (appendRes < 0) goto bad;
    }
    if (0) {
        bad:
        Py_CLEAR(list);  // decref and set list to NULL - this will clean up any of list contents that were already
    }
    return list;
}