2010-10-14 73 views
5

晕,Python引用计数和ctypes

我有一些麻烦理解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。我认为它应该是2getrefcount函数本身就是一个ref)。

现在我不能在C++中返回之前使Py_DECREF()因为对象被删除。我可以减少python中的ref数量吗?

编辑 恰好裁判计数什么时候投函数被调用?我不确定从下面的文档。 http://docs.python.org/library/ctypes.html#ctypes.cast

ctypes.cast(OBJ,类型) 此功能类似于铸造操作中C.它返回类型的新实例,它指向同一个存储块与obj。 type必须是指针类型,obj必须是可以被解释为指针的对象。

回答

4

在进一步的研究中,我发现可以指定函数的返回类型。 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 

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

4

你的C++代码似乎使用official C-API是一个经典的包装,这是一个有点自ctypes的怪异通常用于使用Python中经典的C类型(如整型,浮点,等...)。

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

您不必担心Py_XINCREF/Py_XDECREF(优于Py_INCREF/Py_DECREF因为它接受NULL指针),只有当你想改变对象的所有权:

例如,你已经创建了一个地图的包装在python中(我们称之为类型化对象py_map)。该元素是C++类Foo的元素,并且你为它们创建了另一个python包装器(我们称之为py_Foo)。如果您创建一个包裹[]操作符的功能,你要在Python返回py_Foo对象:

F = py_Map["key"] 

但由于所有权给调用函数,你会调用析构函数,当你删除F并且C++中的映射包含一个指向释放的objet的指针!

解决的办法是用C++写在[]的包装:

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

你应该看一看的borrowed and owned reference在Python的概念。这对于正确理解参考计数器至关重要。

+0

但你创建一个python-c模块吧?我想跳过这一点,并与ctypes你可以只'ctypes.cdll.LoadLibrary('some.so')'。所以返回类型处理是不同的。用ctypes它是'ctypes.py_object'。然后我使用转换函数,并且我不确定引用计数是如何正确处理的。看我的编辑。 – tauran 2010-10-14 09:29:01

+0

的确,这可能会更复杂一点......我试图读取cast()的代码源,但我没有足够的时间正确地调用。如果我找到证明ctypes拥有所有权的东西(如果是这种情况,可能应该考虑直接创建一个python-c对象来消除这些问题),我会尝试着看看有没有足够的时间并编辑我的答案。这很容易,因为你已经在使用PyObject了) – ThR37 2010-10-14 09:42:03