インターナルPython ー PythonのオブジェクトとPyObject構造体の関係

最近,Stackless Pythonの実装を追いかけており,理解したところまでメモがわりに記録しておく.コードを読んでも,詳しいところはすぐ忘れちゃうからね.

読むコードは,Stackless Pythonの3.12(http://www.stackless.com/svn)となる.Stackless Pythonとは言っても,基本的な部分はCPythonと殆ど変わらないので,Pythonを読むときの参考になるのではないかとも思う.

Stackless Pythonの説明はStackless Python入門 - NO!と言えるようになりたいで行ったので,興味があれば参考にして頂きたい.

Pythonのオブジェクトを表す構造体

少し見たところ,Pythonではオブジェクトを表す構造体は,構造体メンバの先頭にPyObject,またはPyVarObjectという構造体を持つようである.たとえば,Pythonで文字列を表すオブジェクトは,str型(Python2.xでいうunicode型)であるが,str型を表す構造体は次のように定義されている.

/* Include/unicodeobject.h */
 437 typedef struct {
 438     PyObject_HEAD    /* PyObject ob_base; と同じ意味 */
 439     Py_ssize_t length;          /* Length of raw Unicode data in buffer */
 440     Py_UNICODE *str;            /* Raw Unicode buffer */
 441     long hash;                  /* Hash value; -1 if not set */
 442     int state;                  /* != 0 if interned. In this case the two
 443                                  * references from the dictionary to this object
 444                                  * are *not* counted in ob_refcnt. */
 445     PyObject *defenc;           /* (Default) Encoded version as Python
 446                                    string, or NULL; this is used for
 447                                    implementing the buffer protocol */
 448 } PyUnicodeObject;

また,list型を表す構造体は以下のようになっている.

/* Include/listobject.h */
  22 typedef struct {
  23     PyObject_VAR_HEAD    /* PyVarObject ob_base; と同じ意味 */
  24     /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
  25     PyObject **ob_item;
  26 
... /* コメント省略 */
  38     Py_ssize_t allocated;
  39 } PyListObject;

PyObject構造体

これらの構造体の先頭には,PyObject_HEAD または,PyObject_VAR_HEAD と宣言されているが,これらはマクロとなっており,以下のように定義されている.

/* Include/object.h */
  80 /* PyObject_HEAD defines the initial segment of every PyObject. */
  81 #define PyObject_HEAD                   PyObject ob_base;
... /* 省略 */
  90 /* PyObject_VAR_HEAD defines the initial segment of all variable-size
  91  * container objects.  These end with a declaration of an array with 1
  92  * element, but enough space is malloc'ed so that the array actually
  93  * has room for ob_size elements.  Note that ob_size is an element count,
  94  * not necessarily a byte count.
  95  */
  96 #define PyObject_VAR_HEAD      PyVarObject ob_base;

これを見て分かるように,PyObject_HEAD では PyObject 型の変数を,PyObject_VAR_HEADでは PyVarObject 型の変数を定義しているだけとなる.これだけ見ると,マクロにしなくても良いように思われるが,わざわざマクロとしているのは,おそらく,変数名を固定したいからじゃないかと予想する.

PyObject 構造体と,PyVarObject 構造体は同様に,Include/object.h にて以下のように定義されている.

/* Include/object.h */
  68 /* Define pointers to support a doubly-linked list of all live heap objects. */
  69 #define _PyObject_HEAD_EXTRA            \
  70         struct _object *_ob_next;       \
  71         struct _object *_ob_prev;
... /* 省略 */
  99 /* Nothing is actually declared to be a PyObject, but every pointer to
 100  * a Python object can be cast to a PyObject*.  This is inheritance built
 101  * by hand.  Similarly every pointer to a variable-size Python object can,
 102  * in addition, be cast to PyVarObject*.
 103  */
 104 typedef struct _object {
 105         _PyObject_HEAD_EXTRA
 106         Py_ssize_t ob_refcnt;
 107         struct _typeobject *ob_type;
 108 } PyObject;
 109 
 110 typedef struct {
 111         PyObject ob_base;
 112         Py_ssize_t ob_size; /* Number of items in variable part */
 113 } PyVarObject;

まず,PyObject 構造体から見てみる.PyObject 構造体の中では,_PyObject_HEAD_EXTRAというマクロが使われており,その後 ob_refcnt と ob_type という変数が定義されいるのが分かる.

_PyObject_HEAD_EXTRA だが,これは上記コードを見て分かるとおり,双方向リンクリストを作成するためのポインタである,_ob_next と _ob_prev を宣言しているに過ぎない.Pythonでは,一端 malloc() で確保したオブジェクト構造体が必要無くなった場合,すぐに free() で開放せずに,一定数をバッファしておき再利用する.これはフリーリストと言う手法であるが,この時に _ob_next と _ob_prev が使われる.フリーリストの詳細については,別の機会に改めて見る予定.

リファレンスカウント

ob_refcnt はリファレンスカウントを保持するための変数となる.ob_refcnt の初期化,加算,減算は,_Py_NewReference,Py_INCREF,Py_DECREF マクロによって行われ,これらマクロは次のように定義されている.

/* Include/object.h */
 663 #define _Py_NewReference(op) (                          \
 664         _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA     \
 665         _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA           \
 666         Py_REFCNT(op) = 1)    /* (((PyObject*)(ob))->ob_refcnt) */
... /* 省略 */
 670 #define _Py_Dealloc(op) (                               \
 671         _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA      \
 672         (*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))
 673 #endif /* !Py_TRACE_REFS */
 674 
 675 #define Py_INCREF(op) (                         \
 676         _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA   \
 677         ((PyObject*)(op))->ob_refcnt++)
 678 
 679 #define Py_DECREF(op)                                   \
 680         do {                                            \
 681             if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA   \
 682                 --((PyObject*)(op))->ob_refcnt != 0)    \
 683                     _Py_CHECK_REFCNT(op)                \
 684             else                                        \
 685                 _Py_Dealloc((PyObject *)(op));          \
 686         } while (0)
_Py_NewReference

まず,_Py_NewReference を見てみる.664 行目の_Py_INC_TPALLOCS(op) マクロでは,そのオブジェクト全体が確保された回数の合計を記録するために,Objects/object.c:139 の inc_count() を呼び出しているだけである.おそらくこれは,デバッグなどの為に用いられると予想されるので,説明は割愛する._Py_INC_REFTOTAL もデバッグように使われるマクロであり,リファレンスカウントの合計をカウントしているだけであり,これも説明しない.

_Py_NewReference マクロの中で重要なのは,Py_REFCNT(op) = 1 だけある.Py_REFCNT もマクロであり,その実態は以下のように定義されている.

/* Include/object.h */
 115 #define Py_REFCNT(ob)           (((PyObject*)(ob))->ob_refcnt)

要するにただ単に,ob_refcnt = 1 と,リファレンスカウントの値を初期化しているだけである.

Py_INCREF

次は,リファレンスカウントを増やす Py_INCREF を見てみる.やはり,671 行目ではデバッグ用のために _Py_INC_REFTOTAL でリファレンスカウントの合計をカウントしている.その後の 672 行目でリファレンスカウントをインクリメントしているが,ここでは Py_REFCNT マクロは使われていない.奇妙である.

Py_DECREF

最後に,リファレンスカウントを減らす Py_DECREF マクロを見てみる.同じく,681 行目の,_Py_DEC_REFTOTAL はリファレンスカウントの合計をカウントするためのデバッグ用マクロとなる.682 行目でリファレンスカウントがデクリメントされている.やはりここでも,Py_REFCNT マクロは使われていない.

リファレンスカウントがデクリメントされた後,その値が 0 で無かった場合,デバッグ時には _Py_CHECK_REFCNT でリファレンスカウントが正の値であるかどうかのチェックが行われる.この詳細については,割愛する.

逆に,値が 0 であった場合は,_Py_Dealloc が呼ばれて,オブジェクトが開放される._Py_Dealloc の671 行目では,デバッグのために _Py_INC_TPFREES マクロを呼び出して,オブジェクトを開放した回数の合計をカウントしていおり,672 行目で,オブジェクト解放用のコールバック関数を呼び出している.

Py_TYPE マクロは,オブジェクトを表す構造体のポインタから ob_type 変数へ変換しているのみで,その定義は次のようになっている.

/* Include/object.h */
 116 #define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

ob_type は _typeobject 構造体の変数であり,_typeobject 構造体の tp_dealloc が,そのオブジェクト用の関数ポインタとなっている.そのため,オブジェクトの開放時には,tp_dealloc が示す関数が呼ばれることになる.

タイプオブジェクト

PyObject構造体の ob_type 変数は,_typeobject 構造体へのポインタとなっている.Pythonでは,型を表す型も,やはりPythonのオブジェクトであり,_typeobject 構造体もオブジェクトと同じように扱う.その証拠に,_typeobject 型の先頭メンバは PyObject 構造体となっている.

概念的には,大体こんな感じとなる.

Python では,オブジェクトが生成されるときは,PyObject 構造体から派生した構造体のインスタンスを生成し,そのインスタンスは,自分の型を表す _typeobject 構造体インスタンスへのポインタを持つ._typeobject 構造体には,型名や,解放など各種関数へのポインタなど様々な情報が含まれる._typeobject 構造体の詳細については,また別の機会に述べる予定.

PyVarObject

PyVarObject 構造体であるが,これは,PyObject 構造体に,Py_ssize_t ob_size; というメンバが追加されただけとなる.この ob_size はオブジェクトが持つ要素の数を示す変数となるらしい.PyVarObject は,リストオブジェクトを表す構造体など,複数の要素数を持つオブジェクトに使われているっぽい.詳細についてはまだ追えていないので,理解でき次第おいおい解説したい.