6

我只是想了解在使用 Python C API 时如何处理引用计数。

我想在 C++ 中调用 Python 函数,如下所示:

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;

pythonScript.py 中的 Python 代码非常简单:

def run():
    return 1

“PyObject_CallFunctionObjArgs”的文档说你得到一个新的引用作为返回值。所以我希望“scriptResult”的引用计数为 1。但是输出是:

scriptResult  != null
print reference count: 72

此外,如果我在不减少引用计数的情况下循环执行此操作,我预计会发生内存泄漏。然而,这似乎不会发生。

有人可以帮我理解吗?

亲切的问候!

4

2 回答 2

5

令人困惑的是,小整数(还有True, False, None, 单字符串等)是内嵌的(“is”运算符对整数的行为异常),这意味着无论在程序中使用或获取它们的任何位置,运行时都会尝试使用相同的对象实例:

>>> 1 is 1
True
>>> 1 + 1 is 2
True
>>> 1000 + 1 is 1001
False

这意味着当您编写 时return 1,您将返回一个已经存在的int对象实例,其中(如您所见)相当多的引用计数。因为相同的实例在其他地方使用,未能取消引用它不会导致内存泄漏。

如果您将脚本更改为return 1001orreturn object()然后您将看到初始引用计数为 1 和内存泄漏。

于 2012-10-25T13:10:57.987 回答
2

ecatmur 是对的,数字和字符串在 Python 中是实习的,所以你可以尝试使用一个简单的object()对象。

一个简单的演示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

多一点:当 CPython 创建一个新对象时,它会调用一个 C 宏_Py_NewReference来将引用计数初始化为 1。然后使用Py_INCREF(op)andPy_DECREF(op)来增加和减少引用计数。

于 2012-10-25T13:35:53.443 回答