Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reference counts in a Python C extension

Tags:

python

c

I'm writing my first C extension to Python and am confused about my reference counts. Here's what I'm trying to do.

I populate a dict in a for loop:

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;

Are my ref counts correct? In the C API docs, it specifically recommends using PyObject_FromXXX functions as arguments to PyTuple_SetItem or PyList_SetItem because they "steal" references.

It's not documented whether PyDict_SetItem steals references. I'm guessing it doesn't in which case, I should do

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

Am I right?

like image 922
longhaulblue Avatar asked Dec 16 '11 00:12

longhaulblue


1 Answers

If you look at the CPython source code (Objects/tupleobject.c) for PyTuple_Pack, you will see that it indeed does increment the reference count on each packed object. If you instead do a PyTuple_New followed by PyTuple_SetItem calls, you will not need to decrement the reference counts since SetItem steals the references.

Finally, you may simply want to use Py_BuildValue("(ss)", "some string", "some other string"); It will build your tuple for you and it will create PyStrings for you: http://docs.python.org/c-api/arg.html#Py_BuildValue

like image 132
Nathan Binkert Avatar answered Sep 27 '22 21:09

Nathan Binkert