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;
}
这在文档中没有记录。它只是说“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;
}