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

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

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

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

`Noddy` クラスの 2番目の例では、`first` 属性と `last` 属性にどんなオブジェクトでも格納できるようになっていました。つまり、 `Noddy` オブジェクトの参照は循環しうるのです:

    >>> 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)
    {
        if (self->first && visit(self->first, arg) < 0)
            return -1;
        if (self->last && visit(self->last, arg) < 0)
            return -1;

        return 0;
    }

    static int
    Noddy_clear(Noddy *self)
    {
        Py_XDECREF(self->first);
        self->first = NULL;
        Py_XDECREF(self->last);
        self->last = NULL;

        return 0;
    }

    static void
    Noddy_dealloc(Noddy* self)
    {
        Noddy_clear(self);
        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 | 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)
    {
        if (self->first && visit(self->first, arg) < 0)
            return -1;
        if (self->last && visit(self->last, arg) < 0)
            return -1;

        return 0;
    }

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

また、循環した参照に含まれた内部オブジェクトを消去するためのメソッドも 提供する必要があります。オブジェクト解放用のメソッドを再実装して、 このメッソドに使いましょう:

    static int
    Noddy_clear(Noddy *self)
    {
        Py_XDECREF(self->first);
        self->first = NULL;
        Py_XDECREF(self->last);
        self->last = NULL;

        return 0;
    }

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

さいごに、`Py_TPFLAGS_HAVE_GC` フラグを クラス定義のフラグに加えます:

        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/

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