読者です 読者をやめる 読者になる 読者になる

ぱいそんのおぶじぇくと-りすとへん(2)「サイズ」「取得」「格納」

Python

listを生成する過程が分かったらいよいよサイズをとったり要素をいれたりとったりするところがどうしているのか知りたいですよね。
では「サイズ」「取得」「格納」
じゃ、sizeから。

listのサイズを返す関数は

Py_ssize_t
PyList_Size(PyObject *op)

です。
Cでオブジェクトを扱うときの基礎ですが、引数のopはselfやthisと思ってください。
これからもこれは変わりません。


返すのはもちろん

return ((PyListObject *)op) -> ob_size;

です。
受け取るのがPyObjectのポインタですから、キャストしてob_sizeを指していますが、
かんたんですね。
普通に保持しているサイズをそのまま返します。


じゃ、要素を取得する場合を見てみましょう。

PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)

がその関数です。
事前チェックは

if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {

で、見慣れたエラー"list index out of range"を発生させます。
0以下は指定できないことになっていますが、
おや、Pythonはlist_object[-1]を受け付けますね。
不思議に思うかもしれませんが、この値が直に渡されている訳ではない、というだけのことです。
気にしないでください。
最も単純な「インデクスを指定してその場所のアイテムを取得」なのでこうなっています。
とりあえず単純にsizeより大きな値を指定した場合はエラーになりますよね。
そして、取得は

return ((PyListObject *)op) -> ob_item[i];

疑問の余地がない程シンプルですね。
ポインタの配列から指定されたインデクス位置にあるポインタを返すだけです。


では、次は参照をセットする場合。

int
PyList_SetItem(register PyObject *op, register Py_ssize_t i,
               register PyObject *newitem)

これもシンプルです。
自身とインデクスと新しくいれるオブジェクトのポインタを受け取るだけです。
取得と同じように格納時も

if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {

で、サイズに収まらないインデクスはエラーになります。

さて、じゃ格納処理自体もかんたんですね。

p = ((PyListObject *)op) -> ob_item + i;

格納すべき配列位置を取得し、

olditem = *p;

古い要素を取り出し、

 *p = newitem;

新しいアイテムを格納。

Py_XDECREF(olditem);

ここで注目。
参照カウンタの出番です。
olditemの参照をデクリメント(−1)しています。


どこからも参照されなくなったオブジェクトを削除するために必要で、
この参照をカウントしている数値が0になったオブジェクトは
もう誰からも見られることはないので消滅します。
ここでは参照されなくなったら、カウンタを減らされるんだなぁ、という認識で構わないと思います。

こうして読んでいると本当にシンプルで綺麗なソースですね。

Py_ssize_t
PyList_Size(PyObject *op)
{
	if (!PyList_Check(op)) {
		PyErr_BadInternalCall();
		return -1;
	}
	else
		return ((PyListObject *)op) -> ob_size;
}

static PyObject *indexerr = NULL;

PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
	if (!PyList_Check(op)) {
		PyErr_BadInternalCall();
		return NULL;
	}
	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
		if (indexerr == NULL)
			indexerr = PyString_FromString(
				"list index out of range");
		PyErr_SetObject(PyExc_IndexError, indexerr);
		return NULL;
	}
	return ((PyListObject *)op) -> ob_item[i];
}

int
PyList_SetItem(register PyObject *op, register Py_ssize_t i,
               register PyObject *newitem)
{
	register PyObject *olditem;
	register PyObject **p;
	if (!PyList_Check(op)) {
		Py_XDECREF(newitem);
		PyErr_BadInternalCall();
		return -1;
	}
	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
		Py_XDECREF(newitem);
		PyErr_SetString(PyExc_IndexError,
				"list assignment index out of range");
		return -1;
	}
	p = ((PyListObject *)op) -> ob_item + i;
	olditem = *p;
	*p = newitem;
	Py_XDECREF(olditem);
	return 0;
}