Python C扩展中的引用计数

12 投票
1 回答
2840 浏览
提问于 2025-04-17 08:28

我正在为Python写我的第一个C扩展,但对引用计数有点困惑。下面是我想做的事情。

我在一个循环中填充一个字典:

mydict = PyDict_New();

for (...)
{
    pair = PyTuple_Pack(2, PyString_FromString("some string"),
          PyString_FromString("some other string"));

    /* at this point, the refcnt for the tuple is 1, the refcnts for the
       2 string items are 2. Because according to the source, PyString_FromString
       does an INCREF, and PyTuple_Pack() does an INCREF on its items
     */

    PyDict_SetItem(mydict, PyString_FromString("some key"), pair);

    /* At this point, the key's refcnt is 2.  PyString_FromString sets it to 1 and 
       PyDict_SetItem INCREF's it. Ditto for pair since PyDict_SetItem also INCREF's
       the value.
     */

    Py_DECREF(pair);

    /* pair's refcnt is 1 which makes sense to me since mydict now owns the tuple, 
       but the refcnt for its items are still at 2.  I don't understand this part.
     */
}

return mydict;

我的引用计数正确吗?在C API的文档中,特别建议在调用PyTuple_SetItemPyList_SetItem时使用PyObject_FromXXX函数,因为它们会“窃取”引用。

但是文档没有说明PyDict_SetItem是否也会窃取引用。我猜它不会,所以我应该这样做:

first = PyString_FromString("some string");
second = PyString_FromString("some other string");
pair = PyTuple_Pack(2, first, second);
Py_DECREF(second);
Py_DECREF(first);

我这样理解对吗?

1 个回答

7

如果你查看CPython的源代码(在Objects/tupleobject.c文件中)里关于PyTuple_Pack的部分,你会发现它确实会对每个打包的对象增加引用计数。换句话说,每当你把一个对象放进元组里,它的引用计数就会加一。

如果你使用PyTuple_New创建一个新的元组,然后再用PyTuple_SetItem来设置里面的内容,你就不需要手动减少引用计数,因为SetItem会“偷走”这些引用,自动处理好。

最后,你也可以直接使用Py_BuildValue("(ss)", "some string", "some other string"); 这个方法。它会帮你构建元组,并且会为你创建PyStrings。想了解更多,可以查看这个链接:http://docs.python.org/c-api/arg.html#Py_BuildValue

撰写回答