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

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

    #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);
        self->ob_type->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;

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

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

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

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

        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 = {
        PyObject_HEAD_INIT(NULL)
        0,                         /*ob_size*/
        "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);
        self->ob_type->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`) をもとに埋めるものです。 ほとんどの型ではデフォルトのメモリ割り当てを使っています。

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

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

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

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

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

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

        return 0;
    }

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

        (initproc)Noddy_init,         /* tp_init */

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

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

    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"]),
             ])