为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

倚天杖 2022-05-11 14:41:44
这个问题很简单,但有助于巩固我的理解。我知道 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 是要走的路),但我想确定。
查看完整描述

2 回答

?
子衿沉夜

TA贡献1828条经验 获得超3个赞

如果有人return_item从 Python 调用函数,他们可能会这样做:


something = Something()

something_else = return_item(something)

del something

如果return_item没有返回传入的参数,而是返回其他内容,您会期望此时something传入的参数应该从内存中释放,因为它的引用计数下降到零。


如果你不Py_INCREF返回相同的对象,那仍然会发生 - 对象的引用计数将下降到 0,并且你将在something_else.


TL;DR:是的,你应该这样做Py_INCREF,因为你通过从函数返回该对象创建了另一个引用。


查看完整回答
反对 回复 2022-05-11
?
米琪卡哇伊

TA贡献1998条经验 获得超6个赞

您不想在返回之前增加对象的引用计数。这样做会造成内存泄漏,从而阻止对象被垃圾回收。


将引用计数增加为“我正在使用此内存。请不要释放它”。当你输入 C 代码时,你从 Python 中“借用”了引用,但是当你退出 C 代码时,你已经完成了对象并且不再需要引用了。


Python 中的变量和底层内存是分开的,这使得它在内存方面有些效率(阅读更多)。另一个答案忽略了分配给something_else增加底层内存的引用计数的事实。您可以使用 自己验证这一点sys.getrefcount。


import sys

something = "hello"

print(sys.getrefcount(something))       # 2 (getrefcount uses a reference)


something_else = something

print(sys.getrefcount(something_else))  # 3 (same memory as something)


del something

print(sys.getrefcount(something_else))  # 2

print(something_else)                   # "hello"

两者都something引用something_else了相同的内存(包含文本“hello”的字符串)。删除一个不会影响另一个。尽管您的代码使用了 C 函数,但基本原理是相同的。尝试用你的 C 函数的两个版本打印出引用计数,它会更清楚。


您不想做的是Py_DECREF在返回对象之前调用。在这种情况下,引用计数可能会降至零,并且返回的东西完全无效。


查看完整回答
反对 回复 2022-05-11
  • 2 回答
  • 0 关注
  • 124 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信