Python CApi 引用计数详细信息

Python CApi Reference Count Details

我正在查看此处的一些示例代码 (https://docs.python.org/2.0/api/refcountDetails.html) 并试图更好地理解其中两个示例之间的区别:第一个示例是:

PyObject *t;

t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyInt_FromLong(1L));
PyTuple_SetItem(t, 1, PyInt_FromLong(2L));
PyTuple_SetItem(t, 2, PyString_FromString("three"));

作者解释说PyTuple_SetItem() 窃取了引用(所以不需要DECREF)。 好的,我明白了。然后作者使用 PySequence_SetItem() 给出了类似的代码,其中 不会窃取引用 ,因此调用者必须 DECREF,并且示例代码如下所示:

PyObject *l, *x;

l = PyList_New(3);
x = PyInt_FromLong(1L);
PySequence_SetItem(l, 0, x); Py_DECREF(x);
x = PyInt_FromLong(2L);
PySequence_SetItem(l, 1, x); Py_DECREF(x);
x = PyString_FromString("three");
PySequence_SetItem(l, 2, x); Py_DECREF(x);
PyObject *l, *x;

我的问题是,如果第二个示例与第一个示例类似 PyTYPE_FromSOMETYPE 如下,会发生什么情况?

PyObject *l;

l = PyList_New(3);
PySequence_SetItem(l, 0, PyInt_FromLong(1L));
PySequence_SetItem(l, 1, PyInt_FromLong(2L));
PySequence_SetItem(l, 2, PyString_FromString("three"));

最后一种情况是良性的,还是会导致内存泄漏(因为 PySequence_SetItem 不会取得 PyInt_FromLong 和 PyString_FromString 创建的引用的所有权, 来电者也没有 DECREF 它)??

它会导致内存泄漏。

创建对象时,它从引用计数 1 开始。只有当引用计数变为 0 时,对象才会被删除。

第一个示例:当您将新对象传递给窃取引用(取得所有权)的函数时,如PyTuple_SetItem,引用计数不会递增,因此它仍然是 1。当元组最终被销毁并减少其所有元素时,计数将降至 0,因此它将被销毁。 一切都很好。

第三个例子:当你将你的新对象传递给一个不窃取引用(创建新引用)的函数时,比如PySequence_SetItem,refcount 递增的,所以它是2。当元组最终被销毁并减少其所有元素时,计数将下降到1,因此它不会被销毁。而且,由于没有其他人再引用它(除非您将它存储在某个地方),所以任何人都无法对它进行 decref。 所以泄露了

第二个例子:当你将新对象传递给一个不窃取引用(创建新引用)的函数时,如PySequence_SetItem,但随后调用Py_DECREF 在其上,引用计数递增到 2 并递减回 1。因此,当元组最终被销毁并递减其所有元素时,计数将降至 0。一切都很好再次.


如果您想知道为什么 Python 会使用任何非窃取函数,您只需要考虑一个不那么微不足道的案例。

如果您想将项目放在 两个 元组而不是一个元组中怎么办?或者,如果您想将它放在一个元组中,但也将它存储在一个 C 静态指针、某个模块的全局变量或其他地方?如果你想将引用计数存储在两个地方,你希望引用计数增加 2,当你的局部变量消失时它们又下降 1。对于您只是创建一些东西并立即将其移交的真正简单的情况,引用窃取功能可以让您避免一次增加和一次减少,并且对于单行来说非常方便。但是对于任何更复杂的事情,这没有意义。