Cython:返回一个<object>PyObject*,它会泄漏吗?

2024-09-29 01:34:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正在用Cython包装一个库。为此,我想重用纯C绑定的一个函数。在

这是基本设置:

  • 在mylib.pxd公司在
  • 旧图书馆
  • 旧图书馆

在mylib.pxd公司我知道:

cdef extern from old_lib.h:
    PyObject* get_pyobject()

然后将old_lib.c作为扩展名中的源文件传递:

^{pr2}$

在mylib.pxd公司,我使用get_pyobject创建一个我想返回的新对象,如下所示:

cdef PyObject* ptr
ptr = get_pyobject()
return <object>ptr

这给了我想要的行为,但我担心这会泄露ptr引用。在

会吗?我很困惑,因为我发现(旧的)引用说您应该自己管理PyObject*引用并相应地调用Py棼u incriff/DECREF,但在Cython FAQ中,它们似乎说:

Note that the lifetime of the object is only bound to its owned references, not to any C pointers that happen to point to it.

这是否意味着每当丢弃返回值时,ptr将被垃圾回收?在

old_lib.c中,流程如下:

PyObject* get_pyobject()
{
     PyTypedObject* typeptr = PyObject_NEW_VAR(MyType, &Type, size)
     fill_attribute(typeptr->attrib)
     return (PyObject*)typeptr
}

其中,PyObject_NEW_VAR是使用PyObject_InitVarpython标准库(在我的版本中是objimpl.h:196)中实现的。因此,返回的引用是一个借用的引用,但是由于使用了PyObject_MALLOC,我想这是对该对象的唯一引用。相关代码:

#define PyObject_NEW_VAR(type, typeobj, n) \
( (type *) PyObject_InitVar( \
       (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE((typeobj),(n)) ),\
       (typeobj), (n)) )

编辑: 我已经检查过了,当使用上面的代码时,sys.getrefcount返回3。据我所知,当我创建对象时,它的refcount为1。然后,当将其强制转换为object时,其refcount被缓冲为2。因此,它永远不会被垃圾回收(除非有一种方法可以删除一个只有一个可访问指针的对象的两个引用计数)和泄漏。 如果我插入PY_DECREF,它仍然有效,并正确返回2。我还花了一些时间直接在Cython中重写该函数,它返回2。在


Tags: to对象getobjectvarlib公司old
1条回答
网友
1楼 · 发布于 2024-09-29 01:34:59

查看old documentationPyObject_NEW_VAR是函数PyObject_NewVar的宏版本,该函数(如@madnethyperister所说)返回一个“新引用”(即refcount为1)。我怀疑你不再鼓励你使用宏,所以它在最近的文档中消失了。在

事实上,它是根据返回“借用引用”的内容来实现的,这一事实可能只应被视为实现细节,而不是意味着它返回“借用引用”本身的内容。在

关于Cython行为,强制转换到<object>会增加引用计数,从而导致内存泄漏。我建议的诊断方法是查看参考计数,如下所示:

from cpython.ref cimport PyObject # somewhere at the top

def whatever_function():
    cdef PyObject* ptr
    ptr = get_pyobject()
    print ptr.ob_refcnt # prints 1
    ret_val = <object>ptr
    print ptr.ob_refcnt # prints 2,
        # but it will only every be decremented to 1, so never be freed
    return ret_val

在修复它方面,你有两个选择-你可以自己减少一次引用计数,或者你可以改变函数的Cython包装

^{pr2}$

(不要担心它与头文件不完全匹配!)。Cython将其解释为“get_pyobject()返回一个新的引用,因此我们不会自己递增它,而是从这里自动处理引用计数。”

相关问题 更多 >