5

你好,

我在理解 python 引用计数时遇到了一些麻烦。我想要做的是使用 ctypes 模块将一个元组从 c++ 返回到 python。

C++:

PyObject* foo(...)
{

  ...
  return Py_BuildValue("(s, s)", value1, value2);
}

Python:

pointer = c_foo(...) # c_foo loaded with ctypes
obj = cast(pointer, py_object).value

我不确定 obj 的引用计数,所以我尝试sys.getrefcount() 并得到了3. 我认为应该是2(这些getrefcount函数本身就是一个 ref)。

现在我不能Py_DECREF()在 C++ 中返回,因为对象被删除了。我可以减少python中的引用计数吗?

编辑 调用 cast 函数时,引用计数会发生什么?从下面的文档中我不太确定。http://docs.python.org/library/ctypes.html#ctypes.cast

ctypes.cast(obj, type) 这个函数类似于 C 中的 cast 运算符。它返回一个新的 type 实例,该实例指向与 obj 相同的内存块。type 必须是指针类型,obj 必须是可以解释为指针的对象。

4

2 回答 2

5

在进一步的研究中,我发现可以指定函数的返回类型。 http://docs.python.org/library/ctypes.html#callback-functions 这使得强制转换过时并且引用计数不再是问题。

clib = ctypes.cdll.LoadLibrary('some.so')
c_foo = clib.c_foo
c_foo.restype = ctypes.py_object

由于没有给出其他答案,我接受我的新解决方案作为答案。

于 2010-10-17T18:26:24.887 回答
4

您的 c++ 代码似乎是使用官方 C-API的经典包装器,这有点奇怪,因为 ctypes 通常用于在 python 中使用经典的 c 类型(如 int、float 等)。

我个人“单独”使用 C-API(没有 ctypes),但根据我的个人经验,在这种情况下您不必担心引用计数器,因为您返回的是带有Py_BuildValue. 当一个函数返回一个对象时,返回对象的所有权被赋予调用函数。

只有当您想更改对象的所有权时,您才需要担心Py_XINCREF/ Py_XDECREF(比Py_INCREF/更好,因为它接受 NULL 指针):Py_DECREF

例如,您在 python 中创建了一个映射的包装器(我们称之为类型化对象 py_map)。该元素属于 c++ 类 Foo,您已经为它们创建了另一个 python 包装器(我们称之为 py_Foo)。如果您创建一个包装 [] 运算符的函数,您将在 python 中返回一个 py_Foo 对象:

F = py_Map["key"]

但是由于所有权已授予调用函数,因此您将在删除时调用析构函数,F并且 c++ 中的映射包含指向已释放对象的指针!

解决方案是在 [] 的包装器中用 C++ 编写:

...
PyObject* result; // My py_Foo object
Py_XINCREF(result); // transfer the ownership
return result;
}

你应该看看python中借用和拥有引用的概念。这对于正确理解引用计数器至关重要。

于 2010-10-14T09:08:15.297 回答