从 Python C 扩展函数返回参数时需要 INCREF?

这个问题很简单,但有助于巩固我的理解。我知道 C 扩展函数的参数保证在 C 代码期间是实时引用(除非手动 DECREFed)。但是,如果我有一个 C 扩展代码返回其参数列表中存在的 PyObject*,我是否需要在返回它之前 INCREF 参数?即,以下两项哪一项是正确的:


static PyObject return_item(PyObject *self, PyObject *item)

{

    // manipulate item

    return item;

}

或者


static PyObject return_item(PyObject *self, PyObject *item)

{

    // manipulate item

    Py_INCREF(item);

    return item;

}

基于https://docs.python.org/3/extending/extending.html#ownership-rules,其中说


从 Python 调用的 C 函数返回的对象引用必须是拥有的引用——所有权从函数转移到其调用者。


并将对象从 C 返回到 Python我认为是后者(INCREFing 是要走的路),但我想确定。


倚天杖
浏览 125回答 2
2回答

子衿沉夜

如果有人return_item从 Python 调用函数,他们可能会这样做:something = Something()something_else = return_item(something)del something如果return_item没有返回传入的参数,而是返回其他内容,您会期望此时something传入的参数应该从内存中释放,因为它的引用计数下降到零。如果你不Py_INCREF返回相同的对象,那仍然会发生 - 对象的引用计数将下降到 0,并且你将在something_else.TL;DR:是的,你应该这样做Py_INCREF,因为你通过从函数返回该对象创建了另一个引用。

米琪卡哇伊

您不想在返回之前增加对象的引用计数。这样做会造成内存泄漏,从而阻止对象被垃圾回收。将引用计数增加为“我正在使用此内存。请不要释放它”。当你输入 C 代码时,你从 Python 中“借用”了引用,但是当你退出 C 代码时,你已经完成了对象并且不再需要引用了。Python 中的变量和底层内存是分开的,这使得它在内存方面有些效率(阅读更多)。另一个答案忽略了分配给something_else增加底层内存的引用计数的事实。您可以使用 自己验证这一点sys.getrefcount。import syssomething = "hello"print(sys.getrefcount(something))       # 2 (getrefcount uses a reference)something_else = somethingprint(sys.getrefcount(something_else))  # 3 (same memory as something)del somethingprint(sys.getrefcount(something_else))  # 2print(something_else)                   # "hello"两者都something引用something_else了相同的内存(包含文本“hello”的字符串)。删除一个不会影响另一个。尽管您的代码使用了 C 函数,但基本原理是相同的。尝试用你的 C 函数的两个版本打印出引用计数,它会更清楚。您不想做的是Py_DECREF在返回对象之前调用。在这种情况下,引用计数可能会降至零,并且返回的东西完全无效。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python