Search code examples
pythonautomatic-ref-countingpython-c-api

Python C API, High reference count for new Object


I'm just trying to understand how to deal with the reference counts when using the Python C API.

I want to call a Python function in C++, like this:

PyObject* script;
PyObject* scriptRun;
PyObject* scriptResult;

// import module
script = PyImport_ImportModule("pythonScript");
// get function objects
scriptRun = PyObject_GetAttrString(script, "run");
// call function without/empty arguments
scriptResult = PyObject_CallFunctionObjArgs(scriptRun, NULL);

if (scriptResult == NULL)
    cout << "scriptResult  = null" << endl;
else
    cout << "scriptResult  != null" << endl;

cout << "print reference count: " << scriptResult->ob_refcnt << endl;

The Python code in pythonScript.py is very simple:

def run():
    return 1

The documentation of "PyObject_CallFunctionObjArgs" says that you get a new reference as return value. So I would expect "scriptResult" to have a reference count of 1. However the output is:

scriptResult  != null
print reference count: 72

Furthermore I would expect a memory leak if I would do this in a loop without decreasing the reference count. However this seems not to happen.

Could someone help me understand?

Kind regards!


Solution

  • ecatmur is right, numbers and strings are interned in Python, so instead you can try with a simple object() object.

    A simple demo with gc:

    import gc
    
    
    def run():
        return 1
    
    s = run()
    print len(gc.get_referrers(s))  # prints a rather big number, 41 in my case
    
    obj = object()
    print len(gc.get_referrers(obj))  # prints 1
    
    lst = [obj]
    print len(gc.get_referrers(obj))  # prints 2
    
    lst = []
    print len(gc.get_referrers(obj))  # prints 1 again
    

    A bit more: when CPython creates a new object, it calls a C macro _Py_NewReference to initialize the reference count to 1. Then uses Py_INCREF(op) and Py_DECREF(op) to increase and decrease the reference count.