現在のコンピュータはメモリサイズが有限である (そしてしばしば厳しく制限されている)ので、参照回数は重要である。 参照回数により、オブジェクトへの参照を持つ異なった場所が何ヶ所 あるか数えられる。そのような場所は他のオブジェクトであったり、 大域的(もしくは静的な)C言語の変数、もしくはあるC言語関数の ローカル変数でありえる。 あるオブジェクトへの参照回数が0になったとき、そのオブジェクトは 解放される。もしそれが他のオブジェクトへの参照を含んでいれば、 それらの参照回数が減らされる。もしこの減算によりそれらの参照 回数が0になれば、それらの他のオブジェクトが次々に解放される かもしれない。これの繰り返しである (ここで、互いに参照するオブジェクトについて明らかな問題がある。 今のところ解決策は``それをするな''ということである)。
参照回数は常に明示的に操作される。
通常の方法は、あるオブジェクトの参照回数を1つ増やすために
マクロPy_INCREF()sizeof(long) >= sizeof(char*)と仮定している)。
そのため、参照回数の増加は単純な操作である。
オブジェクトへのポインタを持つ、すべてのローカル変数について オブジェクトの参照回数を必ずしも増やす必要はない。 理論的には、オブジェクトの参照回数は変数がそれを指すようになった ときに1つ増えて、変数はスコープから外れたときに1つ減少する。 しかしながら、これらの2つは互いに打ち消しあい、最後には 参照回数は変わらない。参照回数を使う唯一の本当の理由は、 我々の変数がそれを指している間は、オブジェクトが解放されないように することである。 もし、少なくとも我々の変数が生きている間は生きている オブジェクトへの少なくとも1つ他の参照が あると知っていれば、一時的に参照回数を増やす必要はない。 これが起こる重要な状況は、Pythonから呼ばれる拡張モジュール中の C言語関数へ、引数として渡されたオブジェクトにある。 呼び出し機構により、呼び出しの間中、全ての引数への参照を 保持していることが保証される。
しかしながら、よくある落し穴は リストからオブジェクトを取り出して、その参照回数を増やさずに しばらくの間それを持ちつづけることである。 何か他の操作がひょっとするとリストからそのオブジェクトを取り除く かもしれない。そして、その参照回数を減らして、それを解放することが あり得る。 本当の危険は、無害に見える操作が、これを行うことができる であろう任意のPythonのコードを呼び出すかもしれないことである。 Py_DECREF()からユーザ側へ戻るような制御を許す コードの経路があり、そのため、ほとんどのどのような操作も 潜在的に危険である。
安全な方法は、常に一般的な操作(名前が"PyObject_", "PyNumber_", "PySequence_"もしくは"PyMapping_"で始まる関数)を 使うことである。 これらの操作は常に、それらが返すオブジェクトの参照回数を増やす。 これは、その結果の用が済んだときにPy_DECREF()を呼ぶ 責任を呼び出し側に残す。これはそのうちに第二の天性になる。
Python C/API関数の参照回数の挙動は、参照の所有権と いう用語で最もよく説明される。 我々は参照を所有することを話しているのであって、決して オブジェクトを所有することでないことに注意せよ。 オブジェクトは常に共有される。ある関数が参照を所有するときは 適切にそれを始末する必要がある -- 所有権を(通常は その呼び出し側へ)譲るか、Py_DECREF()もしくは Py_XDECREF()を呼ぶことで行う。 ある関数がその呼び出し側へ参照の所有権を譲るときは、 呼び出し側は新しい参照を受けとったと言われる。 所有権が何も譲渡されないときは、呼び出し側は参照を借りると 言われる。借りた参照に対しては何もする必要はない。
逆に言えば、ある関数の呼び出しでオブジェクトへの参照をそれに
渡すとき、2つの可能性がある。関数はそのオブジェクトへの参照を
盗むか、そうでないかだ。参照を盗む関数はほとんどない。
2つの有名な例外は
PyList_SetItem()(1, 2, "three")はこのように
見えるかもしれない(今のところエラー処理については忘れる。
これをコードにするもっと良い方法は以下に示されている):
PyObject *t;
t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyInt_FromLong(1L));
PyTuple_SetItem(t, 1, PyInt_FromLong(2L));
PyTuple_SetItem(t, 2, PyString_FromString("three"));
ついでに言えば、PyTuple_SetItem()はタプルのアイテムを 設定する唯一の方法である。PySequence_SetItem()と PyObject_SetItem()は、タプルが変更不可なデータ型なので これを行うことを拒否する。 あなたがあなた自身で作っているタプルには PyTuple_SetItem()だけを使うべきである。
リストを埋めるための同等のコードは PyList_New()とPyList_SetItem()を 使って書くことができる。そのようなコードはまた、 PySequence_SetItem()を使うこともできる。 これはその2つの違い(余分なPy_DECREF()の呼び出し) を説明するものである:
PyObject *l, *x;
l = PyList_New(3);
x = PyInt_FromLong(1L);
PySequence_SetItem(l, 0, x); Py_DECREF(x);
x = PyInt_FromLong(2L);
PySequence_SetItem(l, 1, x); Py_DECREF(x);
x = PyString_FromString("three");
PySequence_SetItem(l, 2, x); Py_DECREF(x);
``推奨された''方法がもっと多くのコードを使うのを奇妙だと 感じるかもしれない。しかしながら、実際には、タプルやリストを 作成して埋めるこれらの方法を滅多に使わないだろう。 汎用の関数Py_BuildValue()があり、それは C言語の値から、書式文字列によって指示されて、たいていの 一般的なオブジェクトを作成することができる。 例えば、上記の2つのコードのブロックは、 次で置き換えられるだろう(これもまたエラー検査を処理している)。
PyObject *t, *l;
t = Py_BuildValue("(iis)", 1, 2, "three");
l = Py_BuildValue("[iis]", 1, 2, "three");
あなたが書いている関数に渡された引数のように、 参照をただ借りているだけのアイテムに PyObject_SetItem()とその仲間を使うことは、 よりもっと一般的である。 そのような場合、参照をあげてしまう(``それを盗ませる'')ために 参照回数を増やす必要がないので、それらの参照回数に関する振舞いは、 もっと健全である。 例えば、この関数はリスト(実際はどの変更可能なシーケンス)の 全てのアイテムを与えられたアイテムに設定する
int set_all(PyObject *target, PyObject *item)
{
int i, n;
n = PyObject_Length(target);
if (n < 0)
return -1;
for (i = 0; i < n; i++) {
if (PyObject_SetItem(target, i, item) < 0)
return -1;
}
return 0;
}
関数の戻り値については状況はわずかに異なっている。 たいていの関数へ参照を渡すことでその参照に対するあなたの所有者の 責任は変わらないが、オブジェクトへの参照を返す多くの関数は あなたにその参照の所有権を与える。 理由は簡単である: 多くの場合、返されたオブジェクトはその場で 作られていて、あなたが得た参照はそのオブジェクトへの唯一の参照である。 そのため、 PyObject_GetItem()とPySequence_GetItem() のような、オブジェクトへの参照を変えす汎用の関数は常に 新しい参照を返す(つまり、呼び出し側はその参照の所有者になる)。
あなたが関数によって返された参照を所有するかどうかは、 どの関数をあなたが呼んだのかだけに依存することを理解する ことが重要である -- 飾りは(つまり、関数への引数として渡される オブジェクトの型の種類)そのことに関わらない。 よって、もしリストからアイテムをPyList_GetItem()を 使って引き出したら、あなたはその参照を所有しない -- しかし、 もし同じリストから同じアイテムを (図らずも全く同じ引数を取る)PySequence_GetItem()を 使って得たなら、あなたは返されたオブジェクトへの参照を所有するのだ。
ここで、整数のリスト中のアイテムの和を計算する関数を
どうやって書けるか、例を示す。
1つはPyList_GetItem()
long sum_list(PyObject *list)
{
int i, n;
long total = 0;
PyObject *item;
n = PyList_Size(list);
if (n < 0)
return -1; /* Not a list */
for (i = 0; i < n; i++) {
item = PyList_GetItem(list, i); /* Can't fail */
if (!PyInt_Check(item)) continue; /* Skip non-integers */
total += PyInt_AsLong(item);
}
return total;
}
long sum_sequence(PyObject *sequence)
{
int i, n;
long total = 0;
PyObject *item;
n = PySequence_Length(sequence);
if (n < 0)
return -1; /* Has no length */
for (i = 0; i < n; i++) {
item = PySequence_GetItem(sequence, i);
if (item == NULL)
return -1; /* Not a sequence, or other failure */
if (PyInt_Check(item))
total += PyInt_AsLong(item);
Py_DECREF(item); /* Discard reference ownership */
}
return total;
}