2. 新しい型を定義する
*********************

前の章でふれたように、Python では拡張モジュールを書くプログラマが
Python のコードから操作できる、新しい型を定義できるようになっています
。ちょうど Python の中核にある文字列やリストをつくれるようなものです。

これはそんなにむずかしくはありません。拡張型のためのコードにはすべて、
一定のパターンが存在しています。しかし始める前に、いくつか細かいことを
理解しておく必要があるでしょう。

注釈: Python 2.2 から、新しい型を定義する方法がかなり変わって (良く
  なって) います。この文書は Python 2.2 およびそれ以降で新しい型をどう
  やって定 義するかについて述べています。古いバージョンの Python をサ
  ポートする 必要がある場合は、 この文書の古い版 を参照してください。


2.1. 基本的なこと
=================

Python ランタイムでは、すべての Python オブジェクトは "PyObject*" 型の
変数として扱います。 "PyObject" はさほど大仰なオブジェクトではなく、単
にオブジェクトに対する参照回数と、そのオブジェクトの「タイプオブジェク
ト (type object)」へのポインタを格納しているだけです。重要な役割を果た
しているのはこのタイプオブジェクトです。つまりタイプオブジェクトは、例
えばあるオブジェクトのある属性が参照されるとか、あるいは別のオブジェク
トとの間で乗算を行うといったときに、どの (C の) 関数を呼び出すかを決定
しているのです。これらの C 関数は「タイプメソッド (type method)」と呼
ばれ、 "[].append" のようなもの (いわゆる「オブジェクトメソッド
(object method)」) とは区別しています。

なので、新しいオブジェクトの型を定義したいときは、新しいタイプオブジェ
クトを作成すればよいわけです。

この手のことは例を見たほうが早いでしょうから、ここに最小限の、しかし完
全な、新しい型を定義するモジュールをあげておきます:

   #include <Python.h>

   typedef struct {
       PyObject_HEAD
       /* Type-specific fields go here. */
   } noddy_NoddyObject;

   static PyTypeObject noddy_NoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "noddy.Noddy",             /* tp_name */
       sizeof(noddy_NoddyObject), /* tp_basicsize */
       0,                         /* tp_itemsize */
       0,                         /* tp_dealloc */
       0,                         /* tp_print */
       0,                         /* tp_getattr */
       0,                         /* tp_setattr */
       0,                         /* tp_compare */
       0,                         /* tp_repr */
       0,                         /* tp_as_number */
       0,                         /* tp_as_sequence */
       0,                         /* tp_as_mapping */
       0,                         /* tp_hash */
       0,                         /* tp_call */
       0,                         /* tp_str */
       0,                         /* tp_getattro */
       0,                         /* tp_setattro */
       0,                         /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT,        /* tp_flags */
       "Noddy objects",           /* tp_doc */
   };

   static PyMethodDef noddy_methods[] = {
       {NULL}  /* Sentinel */
   };

   #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
   #define PyMODINIT_FUNC void
   #endif
   PyMODINIT_FUNC
   initnoddy(void) 
   {
       PyObject* m;

       noddy_NoddyType.tp_new = PyType_GenericNew;
       if (PyType_Ready(&noddy_NoddyType) < 0)
           return;

       m = Py_InitModule3("noddy", noddy_methods,
                          "Example module that creates an extension type.");

       Py_INCREF(&noddy_NoddyType);
       PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);
   }

さしあたって覚えておくことは以上ですが、これで前の章からすこしは説明が
わかりやすくなっていることと思います。

最初に習うのは、つぎのようなものです:

   typedef struct {
       PyObject_HEAD
   } noddy_NoddyObject;

これが Noddy オブジェクトの内容です --- このケースでは、ほかの Python
オブジェクトが持っているものと何ら変わりはありません。つまり参照カウン
トと型オブジェクトへのポインタですね。これらは "PyObject_HEAD" マクロ
によって展開されるメンバです。マクロを使う理由は、レイアウトを標準化す
るためと、デバッグ用ビルド時に特別なデバッグ用のメンバを定義できるよう
にするためです。この "PyObject_HEAD" マクロの後にはセミコロンがないこ
とに注意してください。セミコロンはすでにマクロ内に含まれています。うっ
かり後にセミコロンをつけてしまわないように気をつけて。これはお使いの機
種では何の問題も起こらないかもしれませんが、機種によっては、おそらく問
題になるのです! (Windows 上では、MS Visual C がこの手のエラーを出し、
コンパイルできないことが知られています)

比較のため、以下に標準的な Python の整数型の定義を見てみましょう:

   typedef struct {
       PyObject_HEAD
       long ob_ival;
   } PyIntObject;

では次にいってみます。かなめの部分、タイプオブジェクトです。

   static PyTypeObject noddy_NoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "noddy.Noddy",             /* tp_name */
       sizeof(noddy_NoddyObject), /* tp_basicsize */
       0,                         /* tp_itemsize */
       0,                         /* tp_dealloc */
       0,                         /* tp_print */
       0,                         /* tp_getattr */
       0,                         /* tp_setattr */
       0,                         /* tp_compare */
       0,                         /* tp_repr */
       0,                         /* tp_as_number */
       0,                         /* tp_as_sequence */
       0,                         /* tp_as_mapping */
       0,                         /* tp_hash */
       0,                         /* tp_call */
       0,                         /* tp_str */
       0,                         /* tp_getattro */
       0,                         /* tp_setattro */
       0,                         /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT,        /* tp_flags */
       "Noddy objects",           /* tp_doc */
   };

"object.h" の中にある "PyTypeObject" の定義を見ると、実際にはここに挙
げた以上の数のメンバがあるとわかるでしょう。これ以外のメンバは C コン
パイラによってゼロに初期化されるので、必要な時を除いてふつうはそれらの
値を明示的には指定せずにおきます。

次のものは非常に重要なので、とくに最初の最初に見ておきましょう:

   PyVarObject_HEAD_INIT(NULL, 0)

これはちょっとぶっきらぼうですね。実際に書きたかったのはこうです:

   PyVarObject_HEAD_INIT(&PyType_Type, 0)

この場合、タイプオブジェクトの型は「type」という名前になりますが、これ
は厳密には C の基準に従っておらず、コンパイラによっては文句を言われま
す。幸いにも、このメンバは "PyType_Ready()" が埋めてくれます。

   "noddy.Noddy",              /* tp_name */

これは型の名前です。この名前はオブジェクトのデフォルトの表現形式と、い
くつかのエラーメッセージ中で使われます。たとえば:

   >>> "" + noddy.new_noddy()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: cannot add type "noddy.Noddy" to string

注意: この名前はドットで区切られた名前で、モジュール名とそのモジュール
内での型名の両方を含んでいます。 この場合のモジュールは "noddy" で型は
"Noddy" ですから、ここでの型名としては "noddy.Noddy" を指定するわけで
す。 ドットで区切られていない名前を使うと、文書ツールの pydoc がその新
しい型をモジュールの文書に載せなくなるという副作用があります。

   sizeof(noddy_NoddyObject),  /* tp_basicsize */

これによって Python は "PyObject_New()" が呼ばれたときにどれくらいの量
のメモリを割り当てればよいのか知ることができます。

注釈: あなたのタイプを Python でサブクラス化可能にしたい場合、そのタ
  イプが 基底タイプと同じ "tp_basicsize" をもっていると多重継承のとき
  に問題が 生じることがあります。そのタイプを Python のサブクラスにし
  たとき、そ の "__bases__" リストにはあなたのタイプが最初にくるように
  しなければ なりません。さもないとエラーの発生なしにあなたのタイプの
  "__new__()" メソッドを呼び出すことはできなくなります。この問題を回避
  するには、つ ねにあなたのタイプの "tp_basicsize" をその基底タイプよ
  りも大きくして おくことです。ほとんどの場合、あなたのタイプは
  "object" か、そうでな ければ基底タイプにデータ用のメンバを追加したも
  のでしょうから、したが って大きさはつねに増加するためこの条件は満た
  されています。

   0,                          /* tp_itemsize */

これはリストや文字列などの可変長オブジェクトのためのものです。今のとこ
ろ無視しましょう。

このあとのいくつかのタイプメソッドは使わないのでとばして、クラスのフラ
グ (flags) には "Py_TPFLAGS_DEFAULT" を入れます。

   Py_TPFLAGS_DEFAULT,        /* tp_flags */

すべての型はフラグにこの定数を含めておく必要があります。これは現在のバ
ージョンの Python で定義されているすべてのメンバを許可します。

この型の docstring は "tp_doc" に入れます。

   "Noddy objects",           /* tp_doc */

ここからタイプメソッドに入るわけですが。ここがあなたのオブジェクトが他
と違うところです。でも今回のバージョンでは、これらはどれも実装しないで
おき、あとでこの例をより面白いものに改造することにしましょう。

とりあえずやりたいのは、この "Noddy" オブジェクトを新しく作れるように
することです。オブジェクトの作成を許可するには、 "tp_new" の実装を提供
する必要があります。今回は、 API 関数によって提供されるデフォルトの実
装 "PyType_GenericNew()" を使うだけにしましょう。これを単に "tp_new"
スロットに代入すればよいのですが、これは互換上の理由からできません。プ
ラットフォームやコンパイラによっては、構造体メンバの初期化に別の場所で
定義されている C の関数を代入することはできないのです。なので、この
"tp_new" の値はモジュール初期化用の関数で代入します。 "PyType_Ready()"
を呼ぶ直前です:

   noddy_NoddyType.tp_new = PyType_GenericNew;
   if (PyType_Ready(&noddy_NoddyType) < 0)
       return;

これ以外のタイプメソッドはすべて *NULL* です。これらについては後ほどふ
れます!

このファイル中にある他のものは、どれもおなじみでしょう。 "initnoddy()"
のこれを除いて:

   if (PyType_Ready(&noddy_NoddyType) < 0)
       return;

この関数は、上で *NULL* に指定していた "ob_type" などのいくつものメン
バを埋めて、 "Noddy" 型を初期化します。

   PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);

これはこの型をモジュール中の辞書に埋め込みます。これで、 "Noddy" クラ
スを呼べば "Noddy" インスタンスを作れるようになりました:

   >>> import noddy
   >>> mynoddy = noddy.Noddy()

これだけです! 残るはこれをどうやってビルドするかということです。上のコ
ードを "noddy.c" というファイルに入れて、以下のものを "setup.py" とい
うファイルに入れましょう

   from distutils.core import setup, Extension
   setup(name="noddy", version="1.0",
         ext_modules=[Extension("noddy", ["noddy.c"])])

そして、シェルから以下のように入力します

   $ python setup.py build

これでサブディレクトリの下にファイル "noddy.so" が作成されます。このデ
ィレクトリに移動して Python を起動しましょう。 "import noddy" して
Noddy オブジェクトで遊べるようになっているはずです。

そんなにむずかしくありません、よね?

もちろん、現在の Noddy 型はまだおもしろみに欠けています。何もデータを
持ってないし、何もしてはくれません。継承してサブクラスを作ることさえで
きないのです。


2.1.1. 基本のサンプルにデータとメソッドを追加する
-------------------------------------------------

この基本のサンプルにデータとメソッドを追加してみましょう。ついでに、こ
の型を基底クラスとしても利用できるようにします。ここでは新しいモジュー
ル "noddy2" をつくり、以下の機能を追加します:

   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first; /* first name */
       PyObject *last;  /* last name */
       int number;
   } Noddy;

   static void
   Noddy_dealloc(Noddy* self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject*)self);
   }

   static PyObject *
   Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       Noddy *self;

       self = (Noddy *)type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyString_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->last = PyString_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->number = 0;
       }

       return (PyObject *)self;
   }

   static int
   Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
   {
       PyObject *first=NULL, *last=NULL, *tmp;

       static char *kwlist[] = {"first", "last", "number", NULL};

       if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                         &first, &last,
                                         &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_XDECREF(tmp);
       }

       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_XDECREF(tmp);
       }

       return 0;
   }


   static PyMemberDef Noddy_members[] = {
       {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
        "first name"},
       {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
        "last name"},
       {"number", T_INT, offsetof(Noddy, number), 0,
        "noddy number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Noddy_name(Noddy* self)
   {
       static PyObject *format = NULL;
       PyObject *args, *result;

       if (format == NULL) {
           format = PyString_FromString("%s %s");
           if (format == NULL)
               return NULL;
       }

       if (self->first == NULL) {
           PyErr_SetString(PyExc_AttributeError, "first");
           return NULL;
       }

       if (self->last == NULL) {
           PyErr_SetString(PyExc_AttributeError, "last");
           return NULL;
       }

       args = Py_BuildValue("OO", self->first, self->last);
       if (args == NULL)
           return NULL;

       result = PyString_Format(format, args);
       Py_DECREF(args);

       return result;
   }

   static PyMethodDef Noddy_methods[] = {
       {"name", (PyCFunction)Noddy_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject NoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "noddy.Noddy",             /* tp_name */
       sizeof(Noddy),             /* tp_basicsize */
       0,                         /* tp_itemsize */
       (destructor)Noddy_dealloc, /* tp_dealloc */
       0,                         /* tp_print */
       0,                         /* tp_getattr */
       0,                         /* tp_setattr */
       0,                         /* tp_compare */
       0,                         /* tp_repr */
       0,                         /* tp_as_number */
       0,                         /* tp_as_sequence */
       0,                         /* tp_as_mapping */
       0,                         /* tp_hash */
       0,                         /* tp_call */
       0,                         /* tp_str */
       0,                         /* tp_getattro */
       0,                         /* tp_setattro */
       0,                         /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT |
           Py_TPFLAGS_BASETYPE,   /* tp_flags */
       "Noddy objects",           /* tp_doc */
       0,                         /* tp_traverse */
       0,                         /* tp_clear */
       0,                         /* tp_richcompare */
       0,                         /* tp_weaklistoffset */
       0,                         /* tp_iter */
       0,                         /* tp_iternext */
       Noddy_methods,             /* tp_methods */
       Noddy_members,             /* tp_members */
       0,                         /* tp_getset */
       0,                         /* tp_base */
       0,                         /* tp_dict */
       0,                         /* tp_descr_get */
       0,                         /* tp_descr_set */
       0,                         /* tp_dictoffset */
       (initproc)Noddy_init,      /* tp_init */
       0,                         /* tp_alloc */
       Noddy_new,                 /* tp_new */
   };

   static PyMethodDef module_methods[] = {
       {NULL}  /* Sentinel */
   };

   #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
   #define PyMODINIT_FUNC void
   #endif
   PyMODINIT_FUNC
   initnoddy2(void)
   {
       PyObject* m;

       if (PyType_Ready(&NoddyType) < 0)
           return;

       m = Py_InitModule3("noddy2", module_methods,
                          "Example module that creates an extension type.");

       if (m == NULL)
           return;

       Py_INCREF(&NoddyType);
       PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
   }

このバージョンでは、いくつもの変更をおこないます。

以下の include を追加します:

   #include <structmember.h>

すこしあとでふれますが、この include には属性を扱うための宣言が入って
います。

"Noddy" オブジェクトの構造体の名前は "Noddy" に縮めることにします。タ
イプオブジェクト名は "NoddyType" に縮めます。

これから "Noddy" 型は 3つのデータ属性をもつようになります。 *first* 、
*last* 、および *number* です。 *first* と *last* 属性はファーストネー
ムとラストネームを格納した Python 文字列で、 *number* 属性は整数の値で
す。

これにしたがうと、オブジェクトの構造体は次のようになります:

   typedef struct {
       PyObject_HEAD
       PyObject *first;
       PyObject *last;
       int number;
   } Noddy;

いまや管理すべきデータができたので、オブジェクトの割り当てと解放に際し
てはより慎重になる必要があります。最低限、オブジェクトの解放メソッドが
必要です:

   static void
   Noddy_dealloc(Noddy* self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject*)self);
   }

この関数は "tp_dealloc" メンバに代入されます。

   (destructor)Noddy_dealloc, /*tp_dealloc*/

このメソッドでやっているのは、ふたつの Python 属性の参照カウントを減ら
すことです。 "first" メンバと "last" メンバが *NULL* かもしれないため
、ここでは "Py_XDECREF()" を使いました。このあとそのオブジェクトのタイ
プメソッドである "tp_free" メンバを呼び出しています。ここではオブジェ
クトの型が "NoddyType" とは限らないことに注意してください。なぜなら、
このオブジェクトはサブクラス化したインスタンスかもしれないからです。

ファーストネームとラストネームを空文字列に初期化しておきたいので、新し
いメソッドを追加することにしましょう:

   static PyObject *
   Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       Noddy *self;

       self = (Noddy *)type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyString_FromString("");
           if (self->first == NULL)
             {
               Py_DECREF(self);
               return NULL;
             }

           self->last = PyString_FromString("");
           if (self->last == NULL)
             {
               Py_DECREF(self);
               return NULL;
             }

           self->number = 0;
       }

       return (PyObject *)self;
   }

そしてこれを "tp_new" メンバとしてインストールします:

   Noddy_new,                 /* tp_new */

この新しいメンバはその型のオブジェクトを (初期化するのではなく) 作成す
る責任を負っています。Python ではこのメンバは "__new__()" メソッドとし
て見えています。 "__new__()" メソッドについての詳しい議論は "Unifying
types and classes in Python" という題名の論文を見てください。 new メソ
ッドを実装する理由のひとつは、インスタンス変数の初期値を保証するためで
す。この例でやりたいのは new メソッドが "first" メンバと  "last" メン
バの値を *NULL* でないようにするということです。もしこれらの初期値が
*NULL* でもよいのであれば、先の例でやったように、new メソッドとして
"PyType_GenericNew()" を使うこともできたでしょう。
"PyType_GenericNew()" はすべてのインスタンス変数のメンバを *NULL* にし
ます。

この new メソッドは静的なメソッドで、インスタンスを生成するときにその
型と、型が呼び出されたときの引数が渡され、新しいオブジェクトを作成して
返します。new メソッドはつねに、あらかじめ固定引数 (positional
argument) とキーワード引数を取りますが、これらのメソッドはしばしばそれ
らの引数は無視して初期化メソッドにそのまま渡します。new メソッドはメモ
リ割り当てのために "tp_alloc" メンバを呼び出します。 "tp_alloc" をこち
らで初期化する必要はありません。これは "PyType_Ready()" が基底クラス (
デフォルトでは "object") をもとに埋めるものです。ほとんどの型ではデフ
ォルトのメモリ割り当てを使っています。

注釈: もし協力的な "tp_new" (基底タイプの "tp_new" または
  "__new__()" を呼 んでいるもの) を作りたいのならば、実行時のメソッド
  解決順序をつかって どのメソッドを呼びだすかを決定しようとしては *い
  けません* 。つねに呼 び出す型を静的に決めておき、直接その "tp_new"
  を呼び出すか、あるいは "type->tp_base->tp_new" を経由してください。
  こうしないと、あなたが作 成したタイプの Python サブクラスが他の
  Python で定義されたクラスも継 承している場合にうまく動かない場合があ
  ります。 (とりわけ、そのような サブクラスのインスタンスを
  "TypeError" を出さずに作ることが不可能に なります。)

つぎに初期化用の関数を見てみましょう:

   static int
   Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
   {
       PyObject *first=NULL, *last=NULL, *tmp;

       static char *kwlist[] = {"first", "last", "number", NULL};

       if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                         &first, &last,
                                         &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_XDECREF(tmp);
       }

       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_XDECREF(tmp);
       }

       return 0;
   }

これは "tp_init" メンバに代入されます。

   (initproc)Noddy_init,         /* tp_init */

Python では、 "tp_init" メンバは "__init__()" メソッドとして見えていま
す。このメソッドは、オブジェクトが作成されたあとに、それを初期化する目
的で使われます。 new メソッドとはちがって、初期化用のメソッドは必ず呼
ばれるとは限りません。初期化用のメソッドは、インスタンスの初期値を提供
するのに必要な引数を受けとります。このメソッドはつねに固定引数とキーワ
ード引数を受けとります。

初期化メソッドは複数回呼び出される可能性があります。あなたのオブジェク
トの "__init__()" メソッドは、誰にでも呼び出すことができるからです。こ
のため、新しい値を代入するさいには特別な注意を払う必要があります。たと
えば、 "first" メンバには以下のように代入したくなるかもしれません:

   if (first) {
       Py_XDECREF(self->first);
       Py_INCREF(first);
       self->first = first;
   }

しかしこのやり方は危険です。このタイプでは "first" メンバに入るオブジ
ェクトをなにも限定していないので、どんなオブジェクトでもとり得てしまう
からです。それはこのコードが "first" メンバにアクセスしようとする前に
、そのデストラクタが呼び出されてしまうかもしれないのです。このような可
能性からパラノイア的に身をまもるため、ほとんどの場合メンバへの代入は,
その参照カウントを減らす前におこなってください。こうする必要がないのは
どんな場合でしょうか?

* その参照カウントが 1 より大きいと確信できる場合。

* そのオブジェクトの解放があなたのタイプのコードにコールバックするよ
  う なことが決してない場合 [1] 。

* ガベージコレクションがサポートされていない場合に "tp_dealloc" ハン
  ド ラで参照カウントを減らすとき [2] 。

ここではインスタンス変数を属性として見えるようにしたいのですが、これに
はいくつもの方法があります。もっとも簡単な方法は、メンバの定義を与える
ことです:

   static PyMemberDef Noddy_members[] = {
       {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
        "first name"},
       {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
        "last name"},
       {"number", T_INT, offsetof(Noddy, number), 0,
        "noddy number"},
       {NULL}  /* Sentinel */
   };

そして、この定義を "tp_members" スロットに入れましょう:

   Noddy_members,             /* tp_members */

各メンバの定義はそれぞれ、メンバの名前、型、オフセット、アクセスフラグ
および docstring です。詳しくは後の "総称的な属性を管理する" (総称的な
属性を管理する) の節をご覧ください。

この方法の欠点は、Python 属性に代入できるオブジェクトの型を制限する方
法がないことです。ここではファーストネーム first とラストネーム last
に、ともに文字列が入るよう期待していますが、今のやり方ではどんな
Python オブジェクトも代入できてしまいます。加えてこの属性は削除 (del)
できてしまい、その場合、 C のポインタには *NULL* が設定されます。たと
えもしメンバが *NULL* 以外の値に初期化されるようにしてあったとしても、
属性が削除されればメンバは *NULL* になってしまいます。

ここでは "name()" と呼ばれるメソッドを定義しましょう。これはファースト
ネーム first とラストネーム last を連結した文字列をそのオブジェクトの
名前として返します。

   static PyObject *
   Noddy_name(Noddy* self)
   {
       static PyObject *format = NULL;
       PyObject *args, *result;

       if (format == NULL) {
           format = PyString_FromString("%s %s");
           if (format == NULL)
               return NULL;
       }

       if (self->first == NULL) {
           PyErr_SetString(PyExc_AttributeError, "first");
           return NULL;
       }

       if (self->last == NULL) {
           PyErr_SetString(PyExc_AttributeError, "last");
           return NULL;
       }

       args = Py_BuildValue("OO", self->first, self->last);
       if (args == NULL)
           return NULL;

       result = PyString_Format(format, args);
       Py_DECREF(args);

       return result;
   }

このメソッドは C 関数として実装され、 "Noddy" (あるいは   "Noddy" のサ
ブクラス) のインスタンスを第一引数として受けとります。メソッドはつねに
そのインスタンスを最初の引数として受けとらなければなりません。しばしば
固定引数とキーワード引数も受けとりますが、今回はなにも必要ないので、固
定引数のタプルもキーワード引数の辞書も取らないことにします。このメソッ
ドは Python の以下のメソッドと等価です:

   def name(self):
      return "%s %s" % (self.first, self.last)

"first" メンバと "last" メンバがそれぞれ *NULL* かどうかチェックしなけ
ればならないことに注意してください。これらは削除される可能性があり、そ
の場合値は *NULL* にセットされます。この属性の削除を禁止して、そこに入
れられる値を文字列に限定できればなおいいでしょう。次の節ではこれについ
て扱います。

さて、メソッドを定義したので、ここでメソッド定義用の配列を作成する必要
があります:

   static PyMethodDef Noddy_methods[] = {
       {"name", (PyCFunction)Noddy_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

これを "tp_methods" スロットに入れましょう:

   Noddy_methods,             /* tp_methods */

ここでの "METH_NOARGS" フラグは、そのメソッドが引数を取らないことを宣
言するのに使われています。

最後に、この型を基底クラスとして利用可能にしましょう。上のメソッドは注
意ぶかく書かれているので、これはそのオブジェクトの型が作成されたり利用
される場合についてどんな仮定も置いていません。なので、ここですべきこと
は "Py_TPFLAGS_BASETYPE" をクラス定義のフラグに加えるだけです:

   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/

"initnoddy()" の名前を "initnoddy2()" に変更し、 "Py_InitModule3()" に
渡されるモジュール名を更新します。

さいごに "setup.py" ファイルを更新して新しいモジュールをビルドします:

   from distutils.core import setup, Extension
   setup(name="noddy", version="1.0",
         ext_modules=[
            Extension("noddy", ["noddy.c"]),
            Extension("noddy2", ["noddy2.c"]),
            ])


2.1.2. データ属性をこまかく制御する
-----------------------------------

この節では、 "Noddy" クラスの例にあった "first" と "last" の各属性にた
いして、より精密な制御を提供します。以前のバージョンのモジュールでは、
インスタンス変数の "first" と "last" には文字列以外のものも代入できて
しまい、あまつさえ削除まで可能でした。ここではこれらの属性が必ず文字列
を保持しているようにしましょう。

   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first;
       PyObject *last;
       int number;
   } Noddy;

   static void
   Noddy_dealloc(Noddy* self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject*)self);
   }

   static PyObject *
   Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       Noddy *self;

       self = (Noddy *)type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyString_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->last = PyString_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->number = 0;
       }

       return (PyObject *)self;
   }

   static int
   Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
   {
       PyObject *first=NULL, *last=NULL, *tmp;

       static char *kwlist[] = {"first", "last", "number", NULL};

       if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist,
                                         &first, &last,
                                         &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_DECREF(tmp);
       }

       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_DECREF(tmp);
       }

       return 0;
   }

   static PyMemberDef Noddy_members[] = {
       {"number", T_INT, offsetof(Noddy, number), 0,
        "noddy number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Noddy_getfirst(Noddy *self, void *closure)
   {
       Py_INCREF(self->first);
       return self->first;
   }

   static int
   Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
   {
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
           return -1;
       }

       if (! PyString_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The first attribute value must be a string");
           return -1;
       }

       Py_DECREF(self->first);
       Py_INCREF(value);
       self->first = value;

       return 0;
   }

   static PyObject *
   Noddy_getlast(Noddy *self, void *closure)
   {
       Py_INCREF(self->last);
       return self->last;
   }

   static int
   Noddy_setlast(Noddy *self, PyObject *value, void *closure)
   {
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
           return -1;
       }

       if (! PyString_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The last attribute value must be a string");
           return -1;
       }

       Py_DECREF(self->last);
       Py_INCREF(value);
       self->last = value;

       return 0;
   }

   static PyGetSetDef Noddy_getseters[] = {
       {"first",
        (getter)Noddy_getfirst, (setter)Noddy_setfirst,
        "first name",
        NULL},
       {"last",
        (getter)Noddy_getlast, (setter)Noddy_setlast,
        "last name",
        NULL},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Noddy_name(Noddy* self)
   {
       static PyObject *format = NULL;
       PyObject *args, *result;

       if (format == NULL) {
           format = PyString_FromString("%s %s");
           if (format == NULL)
               return NULL;
       }

       args = Py_BuildValue("OO", self->first, self->last);
       if (args == NULL)
           return NULL;

       result = PyString_Format(format, args);
       Py_DECREF(args);

       return result;
   }

   static PyMethodDef Noddy_methods[] = {
       {"name", (PyCFunction)Noddy_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject NoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "noddy.Noddy",             /* tp_name */
       sizeof(Noddy),             /* tp_basicsize */
       0,                         /* tp_itemsize */
       (destructor)Noddy_dealloc, /* tp_dealloc */
       0,                         /* tp_print */
       0,                         /* tp_getattr */
       0,                         /* tp_setattr */
       0,                         /* tp_compare */
       0,                         /* tp_repr */
       0,                         /* tp_as_number */
       0,                         /* tp_as_sequence */
       0,                         /* tp_as_mapping */
       0,                         /* tp_hash */
       0,                         /* tp_call */
       0,                         /* tp_str */
       0,                         /* tp_getattro */
       0,                         /* tp_setattro */
       0,                         /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT |
           Py_TPFLAGS_BASETYPE,   /* tp_flags */
       "Noddy objects",           /* tp_doc */
       0,                         /* tp_traverse */
       0,                         /* tp_clear */
       0,                         /* tp_richcompare */
       0,                         /* tp_weaklistoffset */
       0,                         /* tp_iter */
       0,                         /* tp_iternext */
       Noddy_methods,             /* tp_methods */
       Noddy_members,             /* tp_members */
       Noddy_getseters,           /* tp_getset */
       0,                         /* tp_base */
       0,                         /* tp_dict */
       0,                         /* tp_descr_get */
       0,                         /* tp_descr_set */
       0,                         /* tp_dictoffset */
       (initproc)Noddy_init,      /* tp_init */
       0,                         /* tp_alloc */
       Noddy_new,                 /* tp_new */
   };

   static PyMethodDef module_methods[] = {
       {NULL}  /* Sentinel */
   };

   #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
   #define PyMODINIT_FUNC void
   #endif
   PyMODINIT_FUNC
   initnoddy3(void)
   {
       PyObject* m;

       if (PyType_Ready(&NoddyType) < 0)
           return;

       m = Py_InitModule3("noddy3", module_methods,
                          "Example module that creates an extension type.");

       if (m == NULL)
           return;

       Py_INCREF(&NoddyType);
       PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
   }

"first" 属性と "last" 属性をよりこまかく制御するためには、カスタムメイ
ドの getter 関数と setter 関数を使います。以下は "first" 属性から値を
取得する関数 (getter) と、この属性に値を格納する関数 (setter) です:

   Noddy_getfirst(Noddy *self, void *closure)
   {
       Py_INCREF(self->first);
       return self->first;
   }

   static int
   Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
   {
     if (value == NULL) {
       PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
       return -1;
     }

     if (! PyString_Check(value)) {
       PyErr_SetString(PyExc_TypeError,
                       "The first attribute value must be a string");
       return -1;
     }

     Py_DECREF(self->first);
     Py_INCREF(value);
     self->first = value;

     return 0;
   }

getter 関数には "Noddy" オブジェクトと「閉包 (closure)」 (これは void
型のポインタです) が渡されます。今回のケースでは閉包は無視します。 (閉
包とは定義データが渡される setter や getter の高度な利用をサポートする
ためのもので、これを使うとたとえば getter と setter をひとまとめにした
関数に、閉包のデータにもとづいて属性を get するか set するか決めさせる
、といったことができます。)

setter 関数には "Noddy" オブジェクトと新しい値、そして閉包が渡されます
。新しい値は *NULL* かもしれず、その場合はこの属性が削除されます。ここ
では属性が削除されたり、その値が文字列でないときにはエラーを発生させる
ようにします。

ここでは "PyGetSetDef" 構造体の配列をつくります:

   static PyGetSetDef Noddy_getseters[] = {
       {"first",
        (getter)Noddy_getfirst, (setter)Noddy_setfirst,
        "first name",
        NULL},
       {"last",
        (getter)Noddy_getlast, (setter)Noddy_setlast,
        "last name",
        NULL},
       {NULL}  /* Sentinel */
   };

そしてこれを "tp_getset" スロットに登録します:

   Noddy_getseters,           /* tp_getset */

これで属性の getter と setter が登録できました。

"PyGetSetDef" 構造体の最後の要素が上で説明した閉包です。今回は閉包は使
わないので *NULL* を渡しています。

また、メンバ定義からはこれらの属性を除いておきましょう:

   static PyMemberDef Noddy_members[] = {
       {"number", T_INT, offsetof(Noddy, number), 0,
        "noddy number"},
       {NULL}  /* Sentinel */
   };

また、ここでは "tp_init" ハンドラも渡されるものとして文字列のみを許可
するように修正する必要があります [3]:

   static int
   Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
   {
       PyObject *first=NULL, *last=NULL, *tmp;

       static char *kwlist[] = {"first", "last", "number", NULL};

       if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist,
                                         &first, &last,
                                         &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_DECREF(tmp);
       }

       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_DECREF(tmp);
       }

       return 0;
   }

これらの変更によって、 "first" メンバと "last" メンバが決して *NULL*
にならないと保証できました。これでほとんどすべてのケースから *NULL* 値
のチェックを除けます。これは "Py_XDECREF()" 呼び出しを "Py_DECREF()"
呼び出しに変えられることを意味します。唯一これを変えられないのはオブジ
ェクト解放メソッド (deallocator) で、なぜならここではコンストラクタに
よるメンバ初期化が失敗している可能性があるからです。

さて、先ほどもしたように、このモジュール初期化関数と初期化関数内にある
モジュール名を変更しましょう。そして "setup.py" ファイルに追加の定義を
くわえます。


2.1.3. 循環ガベージコレクションをサポートする
---------------------------------------------

Python は循環ガベージコレクション機能をもっており、これは不要なオブジ
ェクトを、たとえ参照カウントがゼロでなくても、発見することができます。
これはオブジェクトの参照が循環しているときに起こりえます。たとえば以下
の例を考えてください:

   >>> l = []
   >>> l.append(l)
   >>> del l

この例では、自分自身をふくむリストをつくりました。たとえこのリストを
del しても、それは自分自身への参照をまだ持ちつづけますから、参照カウン
トはゼロにはなりません。嬉しいことに Python には循環ガベージコレクショ
ン機能がありますから、最終的にはこのリストが不要であることを検出し、解
放できます。

In the second version of the "Noddy" example, we allowed any kind of
object to be stored in the "first" or "last" attributes [4]. This
means that "Noddy" objects can participate in cycles:

   >>> import noddy2
   >>> n = noddy2.Noddy()
   >>> l = [n]
   >>> n.first = l

これは実にばかげた例ですが、すくなくとも "Noddy" クラスに循環ガベージ
コレクション機能のサポートを加える口実を与えてくれます。循環ガベージコ
レクションをサポートするには 2つのタイプスロットを埋め、これらのスロッ
トを許可するようにクラス定義のフラグを設定する必要があります:

   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first;
       PyObject *last;
       int number;
   } Noddy;

   static int
   Noddy_traverse(Noddy *self, visitproc visit, void *arg)
   {
       int vret;

       if (self->first) {
           vret = visit(self->first, arg);
           if (vret != 0)
               return vret;
       }
       if (self->last) {
           vret = visit(self->last, arg);
           if (vret != 0)
               return vret;
       }

       return 0;
   }

   static int
   Noddy_clear(Noddy *self)
   {
       PyObject *tmp;

       tmp = self->first;
       self->first = NULL;
       Py_XDECREF(tmp);

       tmp = self->last;
       self->last = NULL;
       Py_XDECREF(tmp);

       return 0;
   }

   static void
   Noddy_dealloc(Noddy* self)
   {
       PyObject_GC_UnTrack(self);
       Noddy_clear(self);
       Py_TYPE(self)->tp_free((PyObject*)self);
   }

   static PyObject *
   Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       Noddy *self;

       self = (Noddy *)type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyString_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->last = PyString_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }

           self->number = 0;
       }

       return (PyObject *)self;
   }

   static int
   Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
   {
       PyObject *first=NULL, *last=NULL, *tmp;

       static char *kwlist[] = {"first", "last", "number", NULL};

       if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                         &first, &last,
                                         &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_XDECREF(tmp);
       }

       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_XDECREF(tmp);
       }

       return 0;
   }


   static PyMemberDef Noddy_members[] = {
       {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
        "first name"},
       {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
        "last name"},
       {"number", T_INT, offsetof(Noddy, number), 0,
        "noddy number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Noddy_name(Noddy* self)
   {
       static PyObject *format = NULL;
       PyObject *args, *result;

       if (format == NULL) {
           format = PyString_FromString("%s %s");
           if (format == NULL)
               return NULL;
       }

       if (self->first == NULL) {
           PyErr_SetString(PyExc_AttributeError, "first");
           return NULL;
       }

       if (self->last == NULL) {
           PyErr_SetString(PyExc_AttributeError, "last");
           return NULL;
       }

       args = Py_BuildValue("OO", self->first, self->last);
       if (args == NULL)
           return NULL;

       result = PyString_Format(format, args);
       Py_DECREF(args);

       return result;
   }

   static PyMethodDef Noddy_methods[] = {
       {"name", (PyCFunction)Noddy_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject NoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "noddy.Noddy",             /* tp_name */
       sizeof(Noddy),             /* tp_basicsize */
       0,                         /* tp_itemsize */
       (destructor)Noddy_dealloc, /* tp_dealloc */
       0,                         /* tp_print */
       0,                         /* tp_getattr */
       0,                         /* tp_setattr */
       0,                         /* tp_compare */
       0,                         /* tp_repr */
       0,                         /* tp_as_number */
       0,                         /* tp_as_sequence */
       0,                         /* tp_as_mapping */
       0,                         /* tp_hash */
       0,                         /* tp_call */
       0,                         /* tp_str */
       0,                         /* tp_getattro */
       0,                         /* tp_setattro */
       0,                         /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT |
           Py_TPFLAGS_BASETYPE |
           Py_TPFLAGS_HAVE_GC,    /* tp_flags */
       "Noddy objects",           /* tp_doc */
       (traverseproc)Noddy_traverse,   /* tp_traverse */
       (inquiry)Noddy_clear,           /* tp_clear */
       0,                         /* tp_richcompare */
       0,                         /* tp_weaklistoffset */
       0,                         /* tp_iter */
       0,                         /* tp_iternext */
       Noddy_methods,             /* tp_methods */
       Noddy_members,             /* tp_members */
       0,                         /* tp_getset */
       0,                         /* tp_base */
       0,                         /* tp_dict */
       0,                         /* tp_descr_get */
       0,                         /* tp_descr_set */
       0,                         /* tp_dictoffset */
       (initproc)Noddy_init,      /* tp_init */
       0,                         /* tp_alloc */
       Noddy_new,                 /* tp_new */
   };

   static PyMethodDef module_methods[] = {
       {NULL}  /* Sentinel */
   };

   #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
   #define PyMODINIT_FUNC void
   #endif
   PyMODINIT_FUNC
   initnoddy4(void)
   {
       PyObject* m;

       if (PyType_Ready(&NoddyType) < 0)
           return;

       m = Py_InitModule3("noddy4", module_methods,
                          "Example module that creates an extension type.");

       if (m == NULL)
           return;

       Py_INCREF(&NoddyType);
       PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
   }

traversal メソッドは循環した参照に含まれる可能性のある内部オブジェクト
へのアクセスを提供します:

   static int
   Noddy_traverse(Noddy *self, visitproc visit, void *arg)
   {
       int vret;

       if (self->first) {
           vret = visit(self->first, arg);
           if (vret != 0)
               return vret;
       }
       if (self->last) {
           vret = visit(self->last, arg);
           if (vret != 0)
               return vret;
       }

       return 0;
   }

循環した参照に含まれるかもしれない各内部オブジェクトに対して、
traversal メソッドに渡された "visit()" 関数を呼びます。 "visit()" 関数
は内部オブジェクトと、traversal メソッドに渡された追加の引数 *arg* を
引数としてとります。この関数はこの値が非負の場合に返される整数の値を返
します。

Python 2.4 以降では、visit 関数の呼び出しを自動化する "Py_VISIT()" マ
クロが用意されています。 "Py_VISIT()" を使えば、 "Noddy_traverse()" は
次のように簡略化できます:

   static int
   Noddy_traverse(Noddy *self, visitproc visit, void *arg)
   {
       Py_VISIT(self->first);
       Py_VISIT(self->last);
       return 0;
   }

注釈: 注意: "tp_traverse" の実装で "Py_VISIT()" を使うには、その引数
  に正確 に *visit* および *arg* という名前をつける必要があります。こ
  れは、こ の退屈な実装に統一性を導入することを促進します。

We also need to provide a method for clearing any subobjects that can
participate in cycles.

   static int
   Noddy_clear(Noddy *self)
   {
       PyObject *tmp;

       tmp = self->first;
       self->first = NULL;
       Py_XDECREF(tmp);

       tmp = self->last;
       self->last = NULL;
       Py_XDECREF(tmp);

       return 0;
   }

"Noddy_clear()" 中での一時変数の使い方に注目してください。ここでは、一
時変数をつかって各メンバの参照カウントを減らす前にそれらに *NULL* を代
入しています。これは次のような理由によります。すでにお話ししたように、
もし参照カウントがゼロになると、このオブジェクトがコールバックされるよ
うになってしまいます。さらに、いまやガベージコレクションをサポートして
いるため、ガベージコレクション時に実行されるコードについても心配しなく
てはなりません。もしガベージコレクションが走っていると、あなたの
"tp_traverse" ハンドラが呼び出される可能性があります。メンバの参照カウ
ントがゼロになった場合に、その値が *NULL* に設定されていないと
"Noddy_traverse()" が呼ばれる機会はありません。

Python 2.4 以降では、注意ぶかく参照カウントを減らすためのマクロ
"Py_CLEAR()" が用意されています。 "Py_CLEAR()" を使えば、
"Noddy_clear()" は次のように簡略化できます:

   static int
   Noddy_clear(Noddy *self)
   {
       Py_CLEAR(self->first);
       Py_CLEAR(self->last);
       return 0;
   }

Note that "Noddy_dealloc()" may call arbitrary functions through
"__del__" method or weakref callback. It means circular GC can be
triggered inside the function.  Since GC assumes reference count is
not zero, we need to untrack the object from GC by calling
"PyObject_GC_UnTrack()" before clearing members. Here is reimplemented
deallocator which uses "PyObject_GC_UnTrack()" and "Noddy_clear()".

   static void
   Noddy_dealloc(Noddy* self)
   {
       PyObject_GC_UnTrack(self);
       Noddy_clear(self);
       Py_TYPE(self)->tp_free((PyObject*)self);
   }

最後に、 "Py_TPFLAGS_HAVE_GC" フラグをクラス定義のフラグに加えます:

   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */

これで完了です。 "tp_alloc" スロットまたは "tp_free" スロットが書かれ
ていれば、それらを循環ガベージコレクションに使えるよう修正すればよいの
です。ほとんどの拡張機能は自動的に提供されるバージョンを使うでしょう。


2.1.4. 他の型のサブクラスを作る
-------------------------------

既存の型を継承した新しい拡張型を作成することができます。組み込み型から
継承するのは特に簡単です。必要な "PyTypeObject" を簡単に利用できるから
です。それに比べて、 "PyTypeObject" 構造体を拡張モジュール間で共有する
のは難しいです。

次の例では、ビルトインの "list" 型を継承した "Shoddy" 型を作成していま
す。新しい型は通常のリスト型と完全に互換性がありますが、追加で内部のカ
ウンタを増やす "increment()" メソッドを持っています。

   >>> import shoddy
   >>> s = shoddy.Shoddy(range(3))
   >>> s.extend(s)
   >>> print len(s)
   6
   >>> print s.increment()
   1
   >>> print s.increment()
   2

   #include <Python.h>

   typedef struct {
       PyListObject list;
       int state;
   } Shoddy;


   static PyObject *
   Shoddy_increment(Shoddy *self, PyObject *unused)
   {
       self->state++;
       return PyInt_FromLong(self->state);
   }


   static PyMethodDef Shoddy_methods[] = {
       {"increment", (PyCFunction)Shoddy_increment, METH_NOARGS,
        PyDoc_STR("increment state counter")},
       {NULL,	NULL},
   };

   static int
   Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds)
   {
       if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0)
           return -1;
       self->state = 0;
       return 0;
   }


   static PyTypeObject ShoddyType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       "shoddy.Shoddy",         /* tp_name */
       sizeof(Shoddy),          /* tp_basicsize */
       0,                       /* tp_itemsize */
       0,                       /* tp_dealloc */
       0,                       /* tp_print */
       0,                       /* tp_getattr */
       0,                       /* tp_setattr */
       0,                       /* tp_compare */
       0,                       /* tp_repr */
       0,                       /* tp_as_number */
       0,                       /* tp_as_sequence */
       0,                       /* tp_as_mapping */
       0,                       /* tp_hash */
       0,                       /* tp_call */
       0,                       /* tp_str */
       0,                       /* tp_getattro */
       0,                       /* tp_setattro */
       0,                       /* tp_as_buffer */
       Py_TPFLAGS_DEFAULT |
           Py_TPFLAGS_BASETYPE, /* tp_flags */
       0,                       /* tp_doc */
       0,                       /* tp_traverse */
       0,                       /* tp_clear */
       0,                       /* tp_richcompare */
       0,                       /* tp_weaklistoffset */
       0,                       /* tp_iter */
       0,                       /* tp_iternext */
       Shoddy_methods,          /* tp_methods */
       0,                       /* tp_members */
       0,                       /* tp_getset */
       0,                       /* tp_base */
       0,                       /* tp_dict */
       0,                       /* tp_descr_get */
       0,                       /* tp_descr_set */
       0,                       /* tp_dictoffset */
       (initproc)Shoddy_init,   /* tp_init */
       0,                       /* tp_alloc */
       0,                       /* tp_new */
   };

   PyMODINIT_FUNC
   initshoddy(void)
   {
       PyObject *m;

       ShoddyType.tp_base = &PyList_Type;
       if (PyType_Ready(&ShoddyType) < 0)
           return;

       m = Py_InitModule3("shoddy", NULL, "Shoddy module");
       if (m == NULL)
           return;

       Py_INCREF(&ShoddyType);
       PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType);
   }

見てわかるように、ソースコードは前の節の "Noddy" の時と非常に似ていま
す。違う部分をそれぞれを見ていきます。

   typedef struct {
       PyListObject list;
       int state;
   } Shoddy;

継承した型のオブジェクトの最初の違いは、親クラスのオブジェクト構造が最
初に必要なことです。基底型が既に "PyObject_HEAD()" を構造体の先頭に持
っています。

Python オブジェクトが "Shoddy" 型のインスタンスだった場合、その
*PyObject** ポインタは *PyListObject** にも *Shoddy** にも安全にキャス
トできます。

   static int
   Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds)
   {
       if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0)
          return -1;
       self->state = 0;
       return 0;
   }

この新しい型の "__init__" メソッドで、基底型の "__init__" メソッドを呼
び出している様子を見ることができます。

このパターンは、カスタムの "new" と "dealloc" メソッドを実装するときに
は重要です。継承した型の "new" メソッドは、 "tp_alloc" を使ってメモリ
を割り当てるべきではありません。それは基底型の "tp_new" を呼出たときに
処理されるからです。

"Shoddy" 型のために "PyTypeObject()" を埋めるとき、 "tp_base()" スロッ
トを見つけることができます。クロスプラットフォームのコンパイラに対応す
るために、直接そのスロットを "PyList_Type()" で埋めてはいけません。代
わりに、後でモジュールの "init()" 関数の中で行うことができます。

   PyMODINIT_FUNC
   initshoddy(void)
   {
       PyObject *m;

       ShoddyType.tp_base = &PyList_Type;
       if (PyType_Ready(&ShoddyType) < 0)
           return;

       m = Py_InitModule3("shoddy", NULL, "Shoddy module");
       if (m == NULL)
           return;

       Py_INCREF(&ShoddyType);
       PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType);
   }

"PyType_Read()" を呼ぶ前に、型の構造は "tp_base" スロットは埋められて
いなければなりません。継承している新しい型を作るとき、 "tp_alloc" スロ
ットを "PyType_GenericNew()" で埋める必要はありません。 -- 基底型のア
ロケート関数が継承されます。

この後は、 "PyType_Ready()" 関数を呼び、タイプオブジェクトをモジュール
へ追加するのは、基本的な "Noddy" の例と同じです。


2.2. タイプメソッド
===================

この節ではさまざまな実装可能なタイプメソッドと、それらが何をするもので
あるかについて、ざっと説明します。

以下は "PyTypeObject" の定義です。デバッグビルドでしか使われないいくつ
かのメンバは省いてあります:

   typedef struct _typeobject {
       PyObject_VAR_HEAD
       char *tp_name; /* For printing, in format "<module>.<name>" */
       int tp_basicsize, tp_itemsize; /* For allocation */

       /* Methods to implement standard operations */

       destructor tp_dealloc;
       printfunc tp_print;
       getattrfunc tp_getattr;
       setattrfunc tp_setattr;
       cmpfunc tp_compare;
       reprfunc tp_repr;

       /* Method suites for standard classes */

       PyNumberMethods *tp_as_number;
       PySequenceMethods *tp_as_sequence;
       PyMappingMethods *tp_as_mapping;

       /* More standard operations (here for binary compatibility) */

       hashfunc tp_hash;
       ternaryfunc tp_call;
       reprfunc tp_str;
       getattrofunc tp_getattro;
       setattrofunc tp_setattro;

       /* Functions to access object as input/output buffer */
       PyBufferProcs *tp_as_buffer;

       /* Flags to define presence of optional/expanded features */
       long tp_flags;

       char *tp_doc; /* Documentation string */

       /* Assigned meaning in release 2.0 */
       /* call function for all accessible objects */
       traverseproc tp_traverse;

       /* delete references to contained objects */
       inquiry tp_clear;

       /* Assigned meaning in release 2.1 */
       /* rich comparisons */
       richcmpfunc tp_richcompare;

       /* weak reference enabler */
       long tp_weaklistoffset;

       /* Added in release 2.2 */
       /* Iterators */
       getiterfunc tp_iter;
       iternextfunc tp_iternext;

       /* Attribute descriptor and subclassing stuff */
       struct PyMethodDef *tp_methods;
       struct PyMemberDef *tp_members;
       struct PyGetSetDef *tp_getset;
       struct _typeobject *tp_base;
       PyObject *tp_dict;
       descrgetfunc tp_descr_get;
       descrsetfunc tp_descr_set;
       long tp_dictoffset;
       initproc tp_init;
       allocfunc tp_alloc;
       newfunc tp_new;
       freefunc tp_free; /* Low-level free-memory routine */
       inquiry tp_is_gc; /* For PyObject_IS_GC */
       PyObject *tp_bases;
       PyObject *tp_mro; /* method resolution order */
       PyObject *tp_cache;
       PyObject *tp_subclasses;
       PyObject *tp_weaklist;

   } PyTypeObject;

*たくさんの* メソッドがありますね。でもそんなに心配する必要はありませ
ん。定義したい型があるなら、実装するのはこのうちのごくわずかですむこと
がほとんどです。

すでに予想されているでしょうが、これらの多様なハンドラについて、これか
らより詳しい情報を提供します。しかしこれらのメンバが構造体中で定義され
ている順番は無視します。というのは、これらのメンバの現れる順序は歴史的
な遺産によるものだからです。型を初期化するさいに、これらのメンバを正し
い順序で並べるよう、くれぐれも注意してください。ふつういちばん簡単なの
は、必要なメンバがすべて含まれている (たとえそれらが "0" に初期化され
ていても) 例をとってきて、自分の型に合わせるよう変更をくわえることです
。

   char *tp_name; /* For printing */

これは型の名前です。前節で説明したように、これはいろいろな場面で現れ、
ほとんどは診断用の目的で使われるものです。なので、そのような場面で役に
立つであろう名前を選んでください!

   int tp_basicsize, tp_itemsize; /* For allocation */

これらのメンバは、この型のオブジェクトが作成されるときにどれだけのメモ
リを割り当てればよいのかをランタイムに指示します。Python には可変長の
構造体 (文字列やリストなどを想像してください) に対する組み込みのサポー
トがある程度あり、ここで "tp_itemsize" メンバが使われます。これらにつ
いてはあとでふれます。

   char *tp_doc;

ここには Python スクリプトリファレンス "obj.__doc__" が doc string を
返すときの文字列 (あるいはそのアドレス) を入れます。

では次に、ほとんどの拡張型が実装するであろう基本的なタイプメソッドに入
っていきます。


2.2.1. ファイナライズとメモリ解放
---------------------------------

   destructor tp_dealloc;

型のインスタンスの参照カウントがゼロになり、Python インタプリタがそれ
を潰して再利用したくなると、この関数が呼ばれます。解放すべきメモリをそ
の型が保持していたり、それ以外にも実行すべき後処理がある場合は、それら
をここに入れられます。オブジェクトそれ自体もここで解放される必要があり
ます。この関数の例は、以下のようなものです:

   static void
   newdatatype_dealloc(newdatatypeobject * obj)
   {
       free(obj->obj_UnderlyingDatatypePtr);
       Py_TYPE(obj)->tp_free(obj);
   }

解放用関数でひとつ重要なのは、処理待ちの例外にいっさい手をつけないこと
です。なぜなら、解放用の関数は Python インタプリタがスタックを元の状態
に戻すときに呼ばれることが多いからです。そして (通常の関数からの復帰で
なく) 例外のためにスタックが巻き戻されるときは、すでに発生している例外
から解放用関数を守るものはありません。解放用の関数がおこなう動作が追加
の Python のコードを実行してしまうと、それらは例外が発生していることを
検知するかもしれません。これはインタプリタが誤解させるエラーを発生させ
ることにつながります。これを防ぐ正しい方法は、安全でない操作を実行する
前に処理待ちの例外を保存しておき、終わったらそれを元に戻すことです。こ
れは "PyErr_Fetch()" および "PyErr_Restore()" 関数を使うことによって可
能になります:

   static void
   my_dealloc(PyObject *obj)
   {
       MyObject *self = (MyObject *) obj;
       PyObject *cbresult;

       if (self->my_callback != NULL) {
           PyObject *err_type, *err_value, *err_traceback;
           int have_error = PyErr_Occurred() ? 1 : 0;

           if (have_error)
               PyErr_Fetch(&err_type, &err_value, &err_traceback);

           cbresult = PyObject_CallObject(self->my_callback, NULL);
           if (cbresult == NULL)
               PyErr_WriteUnraisable(self->my_callback);
           else
               Py_DECREF(cbresult);

           if (have_error)
               PyErr_Restore(err_type, err_value, err_traceback);

           Py_DECREF(self->my_callback);
       }
       Py_TYPE(obj)->tp_free((PyObject*)self);
   }


2.2.2. オブジェクト表現
-----------------------

Python では、オブジェクトの文字列表現を生成するのに 3つのやり方があり
ます: "repr()" 関数 (あるいはそれと等価なバッククォートを用いた表現)
を使う方法、 "str()" 関数を使う方法、そして "print" 文を使う方法です。
ほとんどのオブジェクトで "print" 文は "str()" 関数と同じですが、必要な
場合には特殊なケースとして "FILE*" にも表示できます。 "FILE*" への表示
は、効率が問題となっている場合で、一時的な文字列オブジェクトを作成して
ファイルに書き込むのでは効率が悪すぎることがプロファイリングからも明ら
かな場合にのみ使うべきです。

これらのハンドラはどれも必須ではありません。ほとんどの型ではせいぜい
"tp_str" ハンドラと "tp_repr" ハンドラを実装するだけですみます。

   reprfunc tp_repr;
   reprfunc tp_str;
   printfunc tp_print;

"tp_repr" ハンドラは呼び出されたインスタンスの文字列表現を格納した文字
列オブジェクトを返す必要があります。簡単な例は以下のようなものです:

   static PyObject *
   newdatatype_repr(newdatatypeobject * obj)
   {
       return PyString_FromFormat("Repr-ified_newdatatype{{size:\%d}}",
                                  obj->obj_UnderlyingDatatypePtr->size);
   }

"tp_repr" ハンドラが指定されていなければ、インタプリタはその型の
"tp_name" とそのオブジェクトの一意な識別値をもちいて文字列表現を作成し
ます。

"tp_str" ハンドラと "str()" の関係は、上の "tp_repr" ハンドラと
"repr()" の関係に相当します。つまり、これは Python のコードがオブジェ
クトのインスタンスに対して "str()" を呼び出したときに呼ばれます。この
関数の実装は "tp_repr" ハンドラのそれと非常に似ていますが、得られる文
字列表現は人間が読むことを意図されています。 "tp_str" が指定されていな
い場合、かわりに "tp_repr" ハンドラが使われます。

以下は簡単な例です:

   static PyObject *
   newdatatype_str(newdatatypeobject * obj)
   {
       return PyString_FromFormat("Stringified_newdatatype{{size:\%d}}",
                                  obj->obj_UnderlyingDatatypePtr->size);
   }

print ハンドラは Python がその型のインスタンスを「print する」必要のあ
るときに毎回呼ばれます。たとえば 'node' が TreeNode 型のインスタンスだ
とすると、print ハンドラは Python が以下を実行したときに呼ばれます:

   print node

flags 引数には "Py_PRINT_RAW" というフラグがあり、これはその文字列をク
ォートやおそらくはエスケープシーケンスの解釈もなしで表示することを指示
します。

この print 関数は "FILE*" オブジェクトを引数としてとります。たぶん、こ
こに出力することになるでしょう。

print 関数の例は以下のようになります:

   static int
   newdatatype_print(newdatatypeobject *obj, FILE *fp, int flags)
   {
       if (flags & Py_PRINT_RAW) {
           fprintf(fp, "<{newdatatype object--size: %d}>",
                   obj->obj_UnderlyingDatatypePtr->size);
       }
       else {
           fprintf(fp, "\"<{newdatatype object--size: %d}>\"",
                   obj->obj_UnderlyingDatatypePtr->size);
       }
       return 0;
   }


2.2.3. 属性を管理する
---------------------

属性をもつどのオブジェクトに対しても、その型は、それらオブジェクトの属
性をどのように解決するか制御する関数を提供する必要があります。必要な関
数としては、属性を (それが定義されていれば) 取り出すものと、もうひとつ
は属性に (それが許可されていれば) 値を設定するものです。属性を削除する
のは特殊なケースで、この場合は新しい値としてハンドラに *NULL* が渡され
ます。

Python は 2つの属性ハンドラの組をサポートしています。属性をもつ型はど
ちらか一組を実装するだけでよく、それらの違いは一方の組が属性の名前を
"char*" として受け取るのに対してもう一方の組は属性の名前を "PyObject*"
として受け取る、というものです。それぞれの型はその実装にとって都合がよ
い方を使えます。

   getattrfunc  tp_getattr;        /* char * version */
   setattrfunc  tp_setattr;
   /* ... */
   getattrofunc tp_getattrofunc;   /* PyObject * version */
   setattrofunc tp_setattrofunc;

オブジェクトの属性へのアクセスがつねに (すぐあとで説明する) 単純な操作
だけならば、 "PyObject*" を使って属性を管理する関数として、総称的
(generic) な実装を使えます。特定の型に特化した属性ハンドラの必要性は
Python 2.2 からほとんど完全になくなりました。しかし、多くの例はまだ、
この新しく使えるようになった総称的なメカニズムを使うよう更新されてはい
ません。


2.2.3.1. 総称的な属性を管理する
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

バージョン 2.2 で追加.

ほとんどの型は *単純な* 属性を使うだけです。では、どのような属性が単純
だといえるのでしょうか? それが満たすべき条件はごくわずかです:

1. "PyType_Ready()" が呼ばれたとき、すでに属性の名前がわかっている
   こと 。

2. 属性を参照したり設定したりするときに、特別な記録のための処理が必
   要 でなく、また参照したり設定した値に対してどんな操作も実行する必要
   が ないこと。

これらの条件は、属性の値や、値が計算されるタイミング、または格納された
データがどの程度妥当なものであるかといったことになんら制約を課すもので
はないことに注意してください。

"PyType_Ready()" が呼ばれると、これはそのタイプオブジェクトに参照され
ている 3つのテーブルを使って、そのタイプオブジェクトの辞書中にデスクリ
プタ(*descriptor*) を作成します。各デスクリプタは、インスタンスオブジ
ェクトの属性に対するアクセスを制御します。それぞれのテーブルはなくても
かまいません。もしこれら 3つがすべて *NULL* だと、その型のインスタンス
はその基底型から継承した属性だけを持つことになります。また、
"tp_getattro" および "tp_setattro" が *NULL* のままだった場合も、基底
型にこれらの属性の操作がまかせられます。

テーブルはタイプオブジェクト中の 3つのメンバとして宣言されています:

   struct PyMethodDef *tp_methods;
   struct PyMemberDef *tp_members;
   struct PyGetSetDef *tp_getset;

"tp_methods" が *NULL* でない場合、これは "PyMethodDef" 構造体への配列
を指している必要があります。テーブル中の各エントリは、つぎのような構造
体のインスタンスです:

   typedef struct PyMethodDef {
       const char  *ml_name;       /* method name */
       PyCFunction  ml_meth;       /* implementation function */
       int          ml_flags;      /* flags */
       const char  *ml_doc;        /* docstring */
   } PyMethodDef;

その型が提供する各メソッドについてひとつのエントリを定義する必要があり
ます。基底型から継承してきたメソッドについてはエントリは必要ありません
。これの最後には、配列の終わりを示すための見張り番 (sentinel) として追
加のエントリがひとつ必要です。この場合、 "ml_name" メンバが sentinel
として使われ、その値は *NULL* でなければなりません。

XXX Need to refer to some unified discussion of the structure fields,
shared with the next section.

2番目のテーブルは、インスタンス中に格納されるデータと直接対応づけられ
た属性を定義するのに使います。いくつもの C の原始的な型がサポートされ
ており、アクセスを読み込み専用にも読み書き可能にもできます。このテーブ
ルで使われる構造体は次のように定義されています:

   typedef struct PyMemberDef {
       char *name;
       int   type;
       int   offset;
       int   flags;
       char *doc;
   } PyMemberDef;

このテーブルの各エントリに対してデスクリプタ(*descriptor*)が作成され、
値をインスタンスの構造体から抽出しうる型に対してそれらが追加されます。
"type" メンバは "structmember.h" ヘッダで定義された型のコードをひとつ
含んでいる必要があります。この値は Python における値と C における値を
どのように変換しあうかを定めるものです。 "flags" メンバはこの属性がど
のようにアクセスされるかを制御するフラグを格納するのに使われます。

XXX Need to move some of this to a shared section!

以下のフラグ用定数は "structmember.h" で定義されており、これらはビット
ごとの OR を取って組み合わせられます。

+-----------------------------+------------------------------------------------+
| 定数                        | 意味                                           |
+=============================+================================================+
| "READONLY"                  | 絶対に変更できない。                           |
+-----------------------------+------------------------------------------------+
| "RO"                        | "READONLY" の短縮形。                          |
+-----------------------------+------------------------------------------------+
| "READ_RESTRICTED"           | 制限モード (restricted mode) では参照できない  |
|                             | 。                                             |
+-----------------------------+------------------------------------------------+
| "WRITE_RESTRICTED"          | 制限モード (restricted mode) では変更できない  |
|                             | 。                                             |
+-----------------------------+------------------------------------------------+
| "RESTRICTED"                | 制限モード (restricted mode) では参照も変更も  |
|                             | できない。                                     |
+-----------------------------+------------------------------------------------+

"tp_members" を使ったひとつの面白い利用法は、実行時に使われるデスクリ
プタを作成しておき、単にテーブル中にテキストを置いておくことによって、
この方法で定義されたすべての属性に doc string を関連付けられるようにす
ることです。アプリケーションはこのイントロスペクション用 API を使って
、クラスオブジェクトからデスクリプタを取り出し、その "__doc__" 属性を
使って doc string を得られます。

"tp_methods" テーブルと同じように、ここでも "name" メンバの値を *NULL*
にした見張り用エントリが必要です。


2.2.3.2. 特定の型に特化した属性の管理
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

話を単純にするため、ここでは "char*" を使ったバージョンのみを示します
。name パラメータの型はインターフェイスとして "char*" を使うか
"PyObject*" を使うかの違いしかありません。この例では、上の総称的な例と
同じことを効率的にやりますが、 Python 2.2 で追加された総称的な型のサポ
ートを使わずにやります。これを紹介することは 2つの意味をもっています。
ひとつはどうやって、古いバージョンの Python と互換性のあるやり方で、基
本的な属性管理をおこなうか。そしてもうひとつはハンドラの関数がどのよう
にして呼ばれるのか。これで、たとえその機能を拡張する必要があるとき、何
をどうすればいいかわかるでしょう。

"tp_getattr" ハンドラはオブジェクトが属性への参照を要求するときに呼ば
れます。これは、そのクラスの "__getattr__()" メソッドが呼ばれるであろ
う状況と同じ状況下で呼び出されます。

これを処理するありがちな方法は、(1) 一連の関数 (下の例の
"newdatatype_getSize()" や "newdatatype_setSize()") を実装する、(2) こ
れらの関数を記録したメソッドテーブルを提供する、そして (3) そのテーブ
ルの参照結果を返す getattr 関数を提供することです。メソッドテーブルは
タイプオブジェクトの "tp_methods" メンバと同じ構造を持っています。

以下に例を示します:

   static PyMethodDef newdatatype_methods[] = {
       {"getSize", (PyCFunction)newdatatype_getSize, METH_VARARGS,
        "Return the current size."},
       {"setSize", (PyCFunction)newdatatype_setSize, METH_VARARGS,
        "Set the size."},
       {NULL, NULL, 0, NULL}           /* sentinel */
   };

   static PyObject *
   newdatatype_getattr(newdatatypeobject *obj, char *name)
   {
       return Py_FindMethod(newdatatype_methods, (PyObject *)obj, name);
   }

"tp_setattr" ハンドラは、クラスのインスタンスの "__setattr__()" または
"__delattr__()" メソッドが呼ばれるであろう状況で呼び出されます。ある属
性が削除されるとき、3番目のパラメータは *NULL* になります。以下の例は
たんに例外を発生させるものですが、もし本当にこれと同じことをしたいなら
、 "tp_setattr" ハンドラを *NULL* に設定すべきです。

   static int
   newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v)
   {
       (void)PyErr_Format(PyExc_RuntimeError, "Read-only attribute: \%s", name);
       return -1;
   }


2.2.4. オブジェクトの比較
-------------------------

   cmpfunc tp_compare;

"tp_compare" ハンドラは、オブジェクトどうしの比較が必要で、そのオブジ
ェクトに要求された比較をおこなうのに適した特定の拡張比較メソッドが実装
されていないときに呼び出されます。(これが定義されているとき、
"PyObject_Compare()" または "PyObject_Cmp()" が使われるとこれはつねに
呼び出されます、また Python で "cmp()" が使われたときにも呼び出されま
す。) これは "__cmp__()" メソッドに似ています。この関数はもし *obj1*
が *obj2* より「小さい」場合は "-1" を返し、それらが等しければ "0" 、
そしてもし *obj1* が *obj2* より「大きい」場合は "1" を返す必要があり
ます。 (以前は大小比較の結果として、任意の大きさの負または正の整数を返
せましたが、 Python 2.2 以降ではこれはもう許されていません。将来的には
、上にあげた以外の返り値は別の意味をもつ可能性があります。)

"tp_compare" ハンドラは例外を発生させられます。この場合、この関数は負
の値を返す必要があります。呼び出した側は "PyErr_Occurred()" を使って例
外を検査しなければなりません。

以下はサンプル実装です:

   static int
   newdatatype_compare(newdatatypeobject * obj1, newdatatypeobject * obj2)
   {
       long result;

       if (obj1->obj_UnderlyingDatatypePtr->size <
           obj2->obj_UnderlyingDatatypePtr->size) {
           result = -1;
       }
       else if (obj1->obj_UnderlyingDatatypePtr->size >
                obj2->obj_UnderlyingDatatypePtr->size) {
           result = 1;
       }
       else {
           result = 0;
       }
       return result;
   }


2.2.5. 抽象的なプロトコルのサポート
-----------------------------------

Python はいくつもの *抽象的な* “プロトコル”をサポートしています。これ
らを使用する特定のインターフェイスについては 抽象オブジェクトレイヤ
(abstract objects layer) で解説されています。

これら多数の抽象的なインターフェイスは、Python の実装が開発される初期
の段階で定義されていました。とりわけ数値や辞書、そしてシーケンスなどの
プロトコルは最初から Python の一部だったのです。それ以外のプロトコルは
その後追加されました。型の実装にあるいくつかのハンドラルーチンに依存す
るようなプロトコルのために、古いプロトコルはハンドラの入ったオプション
のブロックとして定義し、型オブジェクトから参照するようになりました。タ
イプオブジェクトの主部に追加のスロットをもつ新しいプロトコルについては
、フラグ用のビットを立てることでそれらのスロットが存在しており、インタ
プリタがチェックすべきであることを指示できます。(このフラグ用のビット
は、そのスロットの値が非 *NULL* であることを示しているわけではありませ
ん。フラグはスロットの存在を示すのに使えますが、そのスロットはまだ埋ま
っていないかもしれないのです。)

   PyNumberMethods   *tp_as_number;
   PySequenceMethods *tp_as_sequence;
   PyMappingMethods  *tp_as_mapping;

お使いのオブジェクトを数値やシーケンス、あるいは辞書のようにふるまうよ
うにしたいならば、それぞれに C の "PyNumberMethods" 構造体、
"PySequenceMethods" 構造体、または "PyMappingMethods" 構造体のアドレス
を入れます。これらに適切な値を入れても入れなくてもかまいません。これら
を使った例は Python の配布ソースにある "Objects" でみつけることができ
るでしょう。

   hashfunc tp_hash;

この関数は、もし使うのならば、これはお使いの型のインスタンスのハッシュ
番号を返すようにします。以下はやや的はずれな例ですが

   static long
   newdatatype_hash(newdatatypeobject *obj)
   {
       long result;
       result = obj->obj_UnderlyingDatatypePtr->size;
       result = result * 3;
       return result;
   }

   ternaryfunc tp_call;

この関数は、その型のインスタンスが「関数として呼び出される」ときに呼ば
れます。たとえばもし "obj1" にそのインスタンスが入っていて、Python ス
クリプトで "obj1('hello')" を実行したとすると、 "tp_call" ハンドラが呼
ばれます。

この関数は 3つの引数をとります:

1. *arg1* にはその呼び出しの対象となる、そのデータ型のインスタンス
   が入 ります。たとえば呼び出しが "obj1('hello')" の場合、 *arg1* は
   "obj1" になります。

2. *arg2* は呼び出しの引数を格納しているタプルです。ここから引数を
   取り 出すには "PyArg_ParseTuple()" を使います。

3. *arg3* はキーワード引数のための辞書です。これが *NULL* 以外でキ
   ーワ ード引数をサポートしているなら、
   "PyArg_ParseTupleAndKeywords()" を つかって引数を取り出せます。キー
   ワード引数をサポートしていないのに これが *NULL* 以外の場合は、キー
   ワード引数はサポートしていない旨の メッセージとともに "TypeError"
   を発生させてください。

以下はこの call 関数をてきとうに使った例です。

   /* Implement the call function.
    *    obj1 is the instance receiving the call.
    *    obj2 is a tuple containing the arguments to the call, in this
    *         case 3 strings.
    */
   static PyObject *
   newdatatype_call(newdatatypeobject *obj, PyObject *args, PyObject *other)
   {
       PyObject *result;
       char *arg1;
       char *arg2;
       char *arg3;

       if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
           return NULL;
       }
       result = PyString_FromFormat(
           "Returning -- value: [\%d] arg1: [\%s] arg2: [\%s] arg3: [\%s]\n",
           obj->obj_UnderlyingDatatypePtr->size,
           arg1, arg2, arg3);
       printf("\%s", PyString_AS_STRING(result));
       return result;
   }

XXX some fields need to be added here...

   /* Added in release 2.2 */
   /* Iterators */
   getiterfunc tp_iter;
   iternextfunc tp_iternext;

これらの関数はイテレータ用プロトコルをサポートします。オブジェクトが、
その (ループ中に順に生成されていくかもしれない) 内容を巡回 (訳注: イテ
レータでひとつずつ要素をたどっていくこと) するイテレータをサポートした
い場合は、 "tp_iter" ハンドラを実装する必要があります。 "tp_iter" ハン
ドラによって返されるオブジェクトは "tp_iter" と "tp_iternext" の両方を
実装する必要があります。どちらのハンドラも、それが呼ばれたインスタンス
をひとつだけ引数としてとり、新しい参照を返します。エラーが起きた場合に
は例外を設定してから *NULL* を返す必要があります。

巡回可能な要素を表現するオブジェクトに対しては、 "tp_iter" ハンドラが
イテレータオブジェクトを返す必要があります。イテレータオブジェクトは巡
回中の状態を保持する責任をもっています。お互いに干渉しない複数のイテレ
ータの存在を許すようなオブジェクト (リストやタプルがそうです) の場合は
、新しいイテレータを作成して返す必要があります。 (巡回の結果生じる副作
用のために) 一回だけしか巡回できないオブジェクトの場合は、それ自身への
参照を返すようなハンドラと、 "tp_iternext" ハンドラも実装する必要があ
ります。ファイルオブジェクトはそのようなイテレータの例です。

イテレータオブジェクトは両方のハンドラを実装する必要があります。
"tp_iter" ハンドラはそのイテレータへの新しい参照を返します (これは破壊
的にしか巡回できないオブジェクトに対する "tp_iter" ハンドラと同じです)
。 "tp_iternext" ハンドラはその次のオブジェクトがある場合、それへの新
しい参照を返します。巡回が終端に達したときは例外を出さずに *NULL* を返
してもいいですし、 "StopIteration" を放出してもかまいません。例外を使
わないほうがやや速度が上がるかもしれません。実際のエラーが起こったとき
には、例外を放出して *NULL* を返す必要があります。


2.2.6. 弱参照(Weak Reference)のサポート
---------------------------------------

Pythonの弱参照実装のひとつのゴールは、どのような（数値のような弱参照に
よる利益を得ない）タイプでもオーバーヘッドなしで弱参照のメカニズムに組
み込めるようにすることです。

弱参照可能なオブジェクトの拡張では、弱参照メカニズムのために
"PyObject*" フィールドをインスタンス構造体に含む必要があります。これは
オブジェクトのコンストラクタで *NULL* に初期化する必要があります。これ
は対応するタイプの "tp_weaklistoffset" フィールドをフィールドのオフセ
ットに設定しなければいけません。たとえば、インスタンスタイプは以下の構
造体で定義されます:

   typedef struct {
       PyObject_HEAD
       PyClassObject *in_class;       /* The class object */
       PyObject      *in_dict;        /* A dictionary */
       PyObject      *in_weakreflist; /* List of weak references */
   } PyInstanceObject;

インスタンス用に静的に宣言されたタイプオブジェクトはこのように定義され
ます:

   PyTypeObject PyInstance_Type = {
       PyObject_HEAD_INIT(&PyType_Type)
       0,
       "module.instance",

       /* Lots of stuff omitted for brevity... */

       Py_TPFLAGS_DEFAULT,                         /* tp_flags */
       0,                                          /* tp_doc */
       0,                                          /* tp_traverse */
       0,                                          /* tp_clear */
       0,                                          /* tp_richcompare */
       offsetof(PyInstanceObject, in_weakreflist), /* tp_weaklistoffset */
   };

タイプのコンストラクタは弱参照を *NULL* に初期化する責任があります:

   static PyObject *
   instance_new() {
       /* Other initialization stuff omitted for brevity */

       self->in_weakreflist = NULL;

       return (PyObject *) self;
   }

ほかに追記すべきことは、デストラクタは弱参照を消すために弱参照のマネー
ジャを呼ぶ必要があることくらいです。これは弱参照リストが *NULL* でない
場合にだけ必要です:

   static void
   instance_dealloc(PyInstanceObject *inst)
   {
       /* Allocate temporaries if needed, but do not begin
          destruction just yet.
        */

       if (inst->in_weakreflist != NULL)
           PyObject_ClearWeakRefs((PyObject *) inst);

       /* Proceed with object destruction normally. */
   }


2.2.7. その他いろいろ
---------------------

上にあげたほとんどの関数は、その値として "0" を与えれば省略できること
を忘れないでください。それぞれの関数で提供しなければならない型の定義が
あり、これらは Python の include 用ディレクトリの "object.h" というフ
ァイルにおさめられています。これは Python の配布ソースに含まれています
。

新しいデータ型に何らかのメソッドを実装するやりかたを学ぶには、以下の方
法がおすすめです: Python の配布されているソースをダウンロードして展開
する。 "Objects" ディレクトリへ行き、C のソースファイルから「 "tp_" 欲
しい名前」の文字列で検索する (たとえば "tp_print" とか "tp_compare" の
ように)。こうすれば実装したい例がみつかるでしょう。

あるオブジェクトが、いま実装している型のインスタンスであるかどうかを確
かめたい場合には、 "PyObject_TypeCheck()" 関数を使ってください。使用例
は以下のようなかんじです:

   if (! PyObject_TypeCheck(some_object, &MyType)) {
       PyErr_SetString(PyExc_TypeError, "arg #1 not a mything");
       return NULL;
   }

-[ 注記 ]-

[1] これはそのオブジェクトが文字列や実数などの基本タイプであるよう
    な時 に成り立ちます。

[2] ここで出てきたタイプではガベージコレクションをサポートしていな
    いの で、この例では "tp_dealloc" ハンドラに依存しています。このハ
    ンドラ はそのタイプがたとえガベージコレクションをサポートしている
    場合でも 、そのオブジェクトの「追跡を解除する」ために呼ばれること
    があります が、これは高度な話題でありここでは扱いません。

[3] first および last メンバが文字列であるということはわかっている
    ので 、いまやそれらの参照カウントを減らすときにはそれほど注意する
    必要は ないように思えるかもしれません。しかし文字列型のサブクラス
    は依然と して受けつけられています。通常の文字列型ならば、解放時に
    あなたのオ ブジェクトがコールバックされることはありませんが、文字
    列型のサブク ラスがそうしないという保証はありません。

[4] 3番目のバージョンでさえ、循環を回避できるという保証はされてい
    ませ ん。たとえ通常の文字列型なら循環しない場合でも、文字列型のサ
    ブクラ スをとることが許されていれば、そのタイプでは循環が発生しう
    るからで す。
