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

混乱は、小さな整数(、、、、1True文字の文字列など)がインターンされることです(「is」演算子は整数で予期せず動作します)。つまり、プログラムで使用または取得される場所はどこでも、ランタイムは使用しようとします。同じオブジェクトインスタンス:FalseNone

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

これは、を書くときに、(これまで見てきたように)かなりの参照カウントを持つreturn 1既存のオブジェクトインスタンスを返すことを意味します。int同じインスタンスが他の場所で使用されているため、逆参照に失敗してもメモリリークは発生しません。

スクリプトをreturn 1001またはreturn 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 に初期化します。次に、 and を使用Py_INCREF(op)Py_DECREF(op)て参照カウントを増減します。

于 2012-10-25T13:35:53.443 に答える