【问题标题】:INCREF needed when returning argument from Python C Extension function?从 Python C 扩展函数返回参数时需要 INCREF?
【发布时间】:2019-08-26 16:13:58
【问题描述】:

这个问题很简单,但有助于巩固我的理解。我知道 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 函数返回的对象引用必须是拥有的引用——所有权从函数转移到其调用者。

Returning objects to Python from C 我认为是后者(INCREFing 是要走的路),但我想确定。

【问题讨论】:

    标签: python python-c-api python-extensions


    【解决方案1】:

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

    something = Something()
    something_else = return_item(something)
    del something
    

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

    如果您不Py_INCREF 并返回相同的对象,这仍然会发生 - 对象的引用计数将降至 0,并且您将在 something_else 中拥有一个无效对象。

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

    【讨论】:

      【解决方案2】:

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

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

      Python 中的变量和底层内存是分开的,这使得它在内存方面有些效率(read 更多)。另一个答案忽略了分配给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"
      

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-10
        相关资源
        最近更新 更多