Python CApi参考计数详细信息

时间:2018-07-09 00:08:00

标签: python python-c-api reference-counting

我在这里查看一些示例代码(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创建的引用的所有权,并且调用者也不会对其进行REFREF)? < / p>

1 个答案:

答案 0 :(得分:4)

这会导致内存泄漏。

创建对象时,它的引用计数为1。仅当引用计数变为0时,对象才会被删除。

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

第三个示例:当您将新对象传递给不窃取引用(创建新引用)的函数时,例如PySequence_SetItem,引用计数递增,因此为2。当元组最终被销毁并对其所有元素进行refref时,计数将降至1,因此不会被销毁。而且,由于没有其他人可以引用它了(除非您将其存储在某个地方),所以任何人都无法对它进行解引用。 所以它被泄漏了。

第二个示例:当您将新对象传递给不会窃取引用(创建新引用)的函数时,例如PySequence_SetItem,然后调用Py_DECREF在其上,refcount递增为2,然后递减为1。因此,当元组最终被销毁并对其所有元素进行refref时,计数将降至0。一切都很好。 < / p>


如果您想知道为什么Python会同时具有任何非偷窃功能,那么您只需要考虑一个比较简单的情况即可。

如果您想将该项目放入两个而不是一个的元组中,该怎么办?或者,如果您想将其放在一个元组中,但又将其存储在C静态指针中,某个模块的全局变量中或其他地方?如果要将引用存储在两个位置,则希望将引用计数增加2,而当您的局部变量消失时,引用计数将减少1。对于仅创建某事物然后立即将其移交的非常简单的情况,引用窃取函数使您可以避免单引号和一引号的使用,并且对单行代码很好用和方便。但是对于更复杂的事情,这没有任何意义。