bpo-33201: Modernize "Extension types" doc (GH-6337)

* bpo-33201: Modernize "Extension types" doc
* Split tutorial and other topics
* Some small fixes
* Address some review comments
* Rename noddy* to custom* and shoddy to sublist
* Fix markup
diff --git a/Doc/includes/custom.c b/Doc/includes/custom.c
new file mode 100644
index 0000000..fb2c7b2
--- /dev/null
+++ b/Doc/includes/custom.c
@@ -0,0 +1,39 @@
+#include <Python.h>
+
+typedef struct {
+    PyObject_HEAD
+    /* Type-specific fields go here. */
+} CustomObject;
+
+static PyTypeObject CustomType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "custom.Custom",
+    .tp_doc = "Custom objects",
+    .tp_basicsize = sizeof(CustomObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_new = PyType_GenericNew,
+};
+
+static PyModuleDef custommodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "custom",
+    .m_doc = "Example module that creates an extension type.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit_custom(void)
+{
+    PyObject *m;
+    if (PyType_Ready(&CustomType) < 0)
+        return NULL;
+
+    m = PyModule_Create(&custommodule);
+    if (m == NULL)
+        return NULL;
+
+    Py_INCREF(&CustomType);
+    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
+    return m;
+}
diff --git a/Doc/includes/custom2.c b/Doc/includes/custom2.c
new file mode 100644
index 0000000..51ab4b8
--- /dev/null
+++ b/Doc/includes/custom2.c
@@ -0,0 +1,132 @@
+#include <Python.h>
+#include "structmember.h"
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *first; /* first name */
+    PyObject *last;  /* last name */
+    int number;
+} CustomObject;
+
+static void
+Custom_dealloc(CustomObject *self)
+{
+    Py_XDECREF(self->first);
+    Py_XDECREF(self->last);
+    Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static PyObject *
+Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CustomObject *self;
+    self = (CustomObject *) type->tp_alloc(type, 0);
+    if (self != NULL) {
+        self->first = PyUnicode_FromString("");
+        if (self->first == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->last = PyUnicode_FromString("");
+        if (self->last == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->number = 0;
+    }
+    return (PyObject *) self;
+}
+
+static int
+Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"first", "last", "number", NULL};
+    PyObject *first = NULL, *last = NULL, *tmp;
+
+    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 Custom_members[] = {
+    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
+     "first name"},
+    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
+     "last name"},
+    {"number", T_INT, offsetof(CustomObject, number), 0,
+     "custom number"},
+    {NULL}  /* Sentinel */
+};
+
+static PyObject *
+Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+{
+    if (self->first == NULL) {
+        PyErr_SetString(PyExc_AttributeError, "first");
+        return NULL;
+    }
+    if (self->last == NULL) {
+        PyErr_SetString(PyExc_AttributeError, "last");
+        return NULL;
+    }
+    return PyUnicode_FromFormat("%S %S", self->first, self->last);
+}
+
+static PyMethodDef Custom_methods[] = {
+    {"name", (PyCFunction) Custom_name, METH_NOARGS,
+     "Return the name, combining the first and last name"
+    },
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject CustomType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "custom2.Custom",
+    .tp_doc = "Custom objects",
+    .tp_basicsize = sizeof(CustomObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+    .tp_new = Custom_new,
+    .tp_init = (initproc) Custom_init,
+    .tp_dealloc = (destructor) Custom_dealloc,
+    .tp_members = Custom_members,
+    .tp_methods = Custom_methods,
+};
+
+static PyModuleDef custommodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "custom2",
+    .m_doc = "Example module that creates an extension type.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit_custom2(void)
+{
+    PyObject *m;
+    if (PyType_Ready(&CustomType) < 0)
+        return NULL;
+
+    m = PyModule_Create(&custommodule);
+    if (m == NULL)
+        return NULL;
+
+    Py_INCREF(&CustomType);
+    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
+    return m;
+}
diff --git a/Doc/includes/custom3.c b/Doc/includes/custom3.c
new file mode 100644
index 0000000..09e8735
--- /dev/null
+++ b/Doc/includes/custom3.c
@@ -0,0 +1,183 @@
+#include <Python.h>
+#include "structmember.h"
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *first; /* first name */
+    PyObject *last;  /* last name */
+    int number;
+} CustomObject;
+
+static void
+Custom_dealloc(CustomObject *self)
+{
+    Py_XDECREF(self->first);
+    Py_XDECREF(self->last);
+    Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static PyObject *
+Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CustomObject *self;
+    self = (CustomObject *) type->tp_alloc(type, 0);
+    if (self != NULL) {
+        self->first = PyUnicode_FromString("");
+        if (self->first == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->last = PyUnicode_FromString("");
+        if (self->last == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->number = 0;
+    }
+    return (PyObject *) self;
+}
+
+static int
+Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"first", "last", "number", NULL};
+    PyObject *first = NULL, *last = NULL, *tmp;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", 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 Custom_members[] = {
+    {"number", T_INT, offsetof(CustomObject, number), 0,
+     "custom number"},
+    {NULL}  /* Sentinel */
+};
+
+static PyObject *
+Custom_getfirst(CustomObject *self, void *closure)
+{
+    Py_INCREF(self->first);
+    return self->first;
+}
+
+static int
+Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
+{
+    PyObject *tmp;
+    if (value == NULL) {
+        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
+        return -1;
+    }
+    if (!PyUnicode_Check(value)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "The first attribute value must be a string");
+        return -1;
+    }
+    tmp = self->first;
+    Py_INCREF(value);
+    self->first = value;
+    Py_DECREF(tmp);
+    return 0;
+}
+
+static PyObject *
+Custom_getlast(CustomObject *self, void *closure)
+{
+    Py_INCREF(self->last);
+    return self->last;
+}
+
+static int
+Custom_setlast(CustomObject *self, PyObject *value, void *closure)
+{
+    PyObject *tmp;
+    if (value == NULL) {
+        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
+        return -1;
+    }
+    if (!PyUnicode_Check(value)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "The last attribute value must be a string");
+        return -1;
+    }
+    tmp = self->last;
+    Py_INCREF(value);
+    self->last = value;
+    Py_DECREF(tmp);
+    return 0;
+}
+
+static PyGetSetDef Custom_getsetters[] = {
+    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
+     "first name", NULL},
+    {"last", (getter) Custom_getlast, (setter) Custom_setlast,
+     "last name", NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyObject *
+Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return PyUnicode_FromFormat("%S %S", self->first, self->last);
+}
+
+static PyMethodDef Custom_methods[] = {
+    {"name", (PyCFunction) Custom_name, METH_NOARGS,
+     "Return the name, combining the first and last name"
+    },
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject CustomType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "custom3.Custom",
+    .tp_doc = "Custom objects",
+    .tp_basicsize = sizeof(CustomObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+    .tp_new = Custom_new,
+    .tp_init = (initproc) Custom_init,
+    .tp_dealloc = (destructor) Custom_dealloc,
+    .tp_members = Custom_members,
+    .tp_methods = Custom_methods,
+    .tp_getset = Custom_getsetters,
+};
+
+static PyModuleDef custommodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "custom3",
+    .m_doc = "Example module that creates an extension type.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit_custom3(void)
+{
+    PyObject *m;
+    if (PyType_Ready(&CustomType) < 0)
+        return NULL;
+
+    m = PyModule_Create(&custommodule);
+    if (m == NULL)
+        return NULL;
+
+    Py_INCREF(&CustomType);
+    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
+    return m;
+}
diff --git a/Doc/includes/custom4.c b/Doc/includes/custom4.c
new file mode 100644
index 0000000..0994d8f
--- /dev/null
+++ b/Doc/includes/custom4.c
@@ -0,0 +1,197 @@
+#include <Python.h>
+#include "structmember.h"
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *first; /* first name */
+    PyObject *last;  /* last name */
+    int number;
+} CustomObject;
+
+static int
+Custom_traverse(CustomObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(self->first);
+    Py_VISIT(self->last);
+    return 0;
+}
+
+static int
+Custom_clear(CustomObject *self)
+{
+    Py_CLEAR(self->first);
+    Py_CLEAR(self->last);
+    return 0;
+}
+
+static void
+Custom_dealloc(CustomObject *self)
+{
+    PyObject_GC_UnTrack(self);
+    Custom_clear(self);
+    Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static PyObject *
+Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CustomObject *self;
+    self = (CustomObject *) type->tp_alloc(type, 0);
+    if (self != NULL) {
+        self->first = PyUnicode_FromString("");
+        if (self->first == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->last = PyUnicode_FromString("");
+        if (self->last == NULL) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        self->number = 0;
+    }
+    return (PyObject *) self;
+}
+
+static int
+Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"first", "last", "number", NULL};
+    PyObject *first = NULL, *last = NULL, *tmp;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", 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 Custom_members[] = {
+    {"number", T_INT, offsetof(CustomObject, number), 0,
+     "custom number"},
+    {NULL}  /* Sentinel */
+};
+
+static PyObject *
+Custom_getfirst(CustomObject *self, void *closure)
+{
+    Py_INCREF(self->first);
+    return self->first;
+}
+
+static int
+Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
+{
+    if (value == NULL) {
+        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
+        return -1;
+    }
+    if (!PyUnicode_Check(value)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "The first attribute value must be a string");
+        return -1;
+    }
+    Py_INCREF(value);
+    Py_CLEAR(self->first);
+    self->first = value;
+    return 0;
+}
+
+static PyObject *
+Custom_getlast(CustomObject *self, void *closure)
+{
+    Py_INCREF(self->last);
+    return self->last;
+}
+
+static int
+Custom_setlast(CustomObject *self, PyObject *value, void *closure)
+{
+    if (value == NULL) {
+        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
+        return -1;
+    }
+    if (!PyUnicode_Check(value)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "The last attribute value must be a string");
+        return -1;
+    }
+    Py_INCREF(value);
+    Py_CLEAR(self->last);
+    self->last = value;
+    return 0;
+}
+
+static PyGetSetDef Custom_getsetters[] = {
+    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
+     "first name", NULL},
+    {"last", (getter) Custom_getlast, (setter) Custom_setlast,
+     "last name", NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyObject *
+Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return PyUnicode_FromFormat("%S %S", self->first, self->last);
+}
+
+static PyMethodDef Custom_methods[] = {
+    {"name", (PyCFunction) Custom_name, METH_NOARGS,
+     "Return the name, combining the first and last name"
+    },
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject CustomType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "custom4.Custom",
+    .tp_doc = "Custom objects",
+    .tp_basicsize = sizeof(CustomObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+    .tp_new = Custom_new,
+    .tp_init = (initproc) Custom_init,
+    .tp_dealloc = (destructor) Custom_dealloc,
+    .tp_traverse = (traverseproc) Custom_traverse,
+    .tp_clear = (inquiry) Custom_clear,
+    .tp_members = Custom_members,
+    .tp_methods = Custom_methods,
+    .tp_getset = Custom_getsetters,
+};
+
+static PyModuleDef custommodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "custom4",
+    .m_doc = "Example module that creates an extension type.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit_custom4(void)
+{
+    PyObject *m;
+    if (PyType_Ready(&CustomType) < 0)
+        return NULL;
+
+    m = PyModule_Create(&custommodule);
+    if (m == NULL)
+        return NULL;
+
+    Py_INCREF(&CustomType);
+    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
+    return m;
+}
diff --git a/Doc/includes/noddy.c b/Doc/includes/noddy.c
deleted file mode 100644
index 07b5d5a..0000000
--- a/Doc/includes/noddy.c
+++ /dev/null
@@ -1,72 +0,0 @@
-#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_reserved */
-    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 */
-    0,                         /* tp_traverse */
-    0,                         /* tp_clear */
-    0,                         /* tp_richcompare */
-    0,                         /* tp_weaklistoffset */
-    0,                         /* tp_iter */
-    0,                         /* tp_iternext */
-    0,                         /* 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 */
-    0,                         /* tp_init */
-    0,                         /* tp_alloc */
-    PyType_GenericNew,         /* tp_new */
-};
-
-static PyModuleDef noddymodule = {
-    PyModuleDef_HEAD_INIT,
-    "noddy",
-    "Example module that creates an extension type.",
-    -1,
-    NULL, NULL, NULL, NULL, NULL
-};
-
-PyMODINIT_FUNC
-PyInit_noddy(void)
-{
-    PyObject* m;
-
-    if (PyType_Ready(&noddy_NoddyType) < 0)
-        return NULL;
-
-    m = PyModule_Create(&noddymodule);
-    if (m == NULL)
-        return NULL;
-
-    Py_INCREF(&noddy_NoddyType);
-    PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);
-    return m;
-}
diff --git a/Doc/includes/noddy2.c b/Doc/includes/noddy2.c
deleted file mode 100644
index 9641558..0000000
--- a/Doc/includes/noddy2.c
+++ /dev/null
@@ -1,172 +0,0 @@
-#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 = PyUnicode_FromString("");
-        if (self->first == NULL) {
-            Py_DECREF(self);
-            return NULL;
-        }
-
-        self->last = PyUnicode_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)
-{
-    if (self->first == NULL) {
-        PyErr_SetString(PyExc_AttributeError, "first");
-        return NULL;
-    }
-
-    if (self->last == NULL) {
-        PyErr_SetString(PyExc_AttributeError, "last");
-        return NULL;
-    }
-
-    return PyUnicode_FromFormat("%S %S", self->first, self->last);
-}
-
-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_reserved */
-    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 PyModuleDef noddy2module = {
-    PyModuleDef_HEAD_INIT,
-    "noddy2",
-    "Example module that creates an extension type.",
-    -1,
-    NULL, NULL, NULL, NULL, NULL
-};
-
-PyMODINIT_FUNC
-PyInit_noddy2(void)
-{
-    PyObject* m;
-
-    if (PyType_Ready(&NoddyType) < 0)
-        return NULL;
-
-    m = PyModule_Create(&noddy2module);
-    if (m == NULL)
-        return NULL;
-
-    Py_INCREF(&NoddyType);
-    PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
-    return m;
-}
diff --git a/Doc/includes/noddy3.c b/Doc/includes/noddy3.c
deleted file mode 100644
index 8a5a753..0000000
--- a/Doc/includes/noddy3.c
+++ /dev/null
@@ -1,225 +0,0 @@
-#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 = PyUnicode_FromString("");
-        if (self->first == NULL) {
-            Py_DECREF(self);
-            return NULL;
-        }
-
-        self->last = PyUnicode_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 (! PyUnicode_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 (! PyUnicode_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)
-{
-    return PyUnicode_FromFormat("%S %S", self->first, self->last);
-}
-
-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_reserved */
-    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 PyModuleDef noddy3module = {
-    PyModuleDef_HEAD_INIT,
-    "noddy3",
-    "Example module that creates an extension type.",
-    -1,
-    NULL, NULL, NULL, NULL, NULL
-};
-
-PyMODINIT_FUNC
-PyInit_noddy3(void)
-{
-    PyObject* m;
-
-    if (PyType_Ready(&NoddyType) < 0)
-        return NULL;
-
-    m = PyModule_Create(&noddy3module);
-    if (m == NULL)
-        return NULL;
-
-    Py_INCREF(&NoddyType);
-    PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
-    return m;
-}
diff --git a/Doc/includes/noddy4.c b/Doc/includes/noddy4.c
deleted file mode 100644
index 08ba4c3..0000000
--- a/Doc/includes/noddy4.c
+++ /dev/null
@@ -1,208 +0,0 @@
-#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 = PyUnicode_FromString("");
-        if (self->first == NULL) {
-            Py_DECREF(self);
-            return NULL;
-        }
-
-        self->last = PyUnicode_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)
-{
-    if (self->first == NULL) {
-        PyErr_SetString(PyExc_AttributeError, "first");
-        return NULL;
-    }
-
-    if (self->last == NULL) {
-        PyErr_SetString(PyExc_AttributeError, "last");
-        return NULL;
-    }
-
-    return PyUnicode_FromFormat("%S %S", self->first, self->last);
-}
-
-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_reserved */
-    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 PyModuleDef noddy4module = {
-    PyModuleDef_HEAD_INIT,
-    "noddy4",
-    "Example module that creates an extension type.",
-    -1,
-    NULL, NULL, NULL, NULL, NULL
-};
-
-PyMODINIT_FUNC
-PyInit_noddy4(void)
-{
-    PyObject* m;
-
-    if (PyType_Ready(&NoddyType) < 0)
-        return NULL;
-
-    m = PyModule_Create(&noddy4module);
-    if (m == NULL)
-        return NULL;
-
-    Py_INCREF(&NoddyType);
-    PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
-    return m;
-}
diff --git a/Doc/includes/shoddy.c b/Doc/includes/shoddy.c
deleted file mode 100644
index 0ef4765..0000000
--- a/Doc/includes/shoddy.c
+++ /dev/null
@@ -1,99 +0,0 @@
-#include <Python.h>
-
-typedef struct {
-    PyListObject list;
-    int state;
-} Shoddy;
-
-
-static PyObject *
-Shoddy_increment(Shoddy *self, PyObject *unused)
-{
-    self->state++;
-    return PyLong_FromLong(self->state);
-}
-
-
-static PyMethodDef Shoddy_methods[] = {
-    {"increment", (PyCFunction)Shoddy_increment, METH_NOARGS,
-     PyDoc_STR("increment state counter")},
-    {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_reserved */
-    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 */
-};
-
-static PyModuleDef shoddymodule = {
-    PyModuleDef_HEAD_INIT,
-    "shoddy",
-    "Shoddy module",
-    -1,
-    NULL, NULL, NULL, NULL, NULL
-};
-
-PyMODINIT_FUNC
-PyInit_shoddy(void)
-{
-    PyObject *m;
-
-    ShoddyType.tp_base = &PyList_Type;
-    if (PyType_Ready(&ShoddyType) < 0)
-        return NULL;
-
-    m = PyModule_Create(&shoddymodule);
-    if (m == NULL)
-        return NULL;
-
-    Py_INCREF(&ShoddyType);
-    PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType);
-    return m;
-}
diff --git a/Doc/includes/sublist.c b/Doc/includes/sublist.c
new file mode 100644
index 0000000..376dddf
--- /dev/null
+++ b/Doc/includes/sublist.c
@@ -0,0 +1,63 @@
+#include <Python.h>
+
+typedef struct {
+    PyListObject list;
+    int state;
+} SubListObject;
+
+static PyObject *
+SubList_increment(SubListObject *self, PyObject *unused)
+{
+    self->state++;
+    return PyLong_FromLong(self->state);
+}
+
+static PyMethodDef SubList_methods[] = {
+    {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
+     PyDoc_STR("increment state counter")},
+    {NULL},
+};
+
+static int
+SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
+{
+    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
+        return -1;
+    self->state = 0;
+    return 0;
+}
+
+static PyTypeObject SubListType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "sublist.SubList",
+    .tp_doc = "SubList objects",
+    .tp_basicsize = sizeof(SubListObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+    .tp_init = (initproc) SubList_init,
+    .tp_methods = SubList_methods,
+};
+
+static PyModuleDef sublistmodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "sublist",
+    .m_doc = "Example module that creates an extension type.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit_sublist(void)
+{
+    PyObject *m;
+    SubListType.tp_base = &PyList_Type;
+    if (PyType_Ready(&SubListType) < 0)
+        return NULL;
+
+    m = PyModule_Create(&sublistmodule);
+    if (m == NULL)
+        return NULL;
+
+    Py_INCREF(&SubListType);
+    PyModule_AddObject(m, "SubList", (PyObject *) &SubListType);
+    return m;
+}
diff --git a/Doc/includes/test.py b/Doc/includes/test.py
index 9e9d4a6..09ebe3f 100644
--- a/Doc/includes/test.py
+++ b/Doc/includes/test.py
@@ -1,181 +1,168 @@
-"""Test module for the noddy examples
+"""Test module for the custom examples
 
-Noddy 1:
+Custom 1:
 
->>> import noddy
->>> n1 = noddy.Noddy()
->>> n2 = noddy.Noddy()
->>> del n1
->>> del n2
+>>> import custom
+>>> c1 = custom.Custom()
+>>> c2 = custom.Custom()
+>>> del c1
+>>> del c2
 
 
-Noddy 2
+Custom 2
 
->>> import noddy2
->>> n1 = noddy2.Noddy('jim', 'fulton', 42)
->>> n1.first
+>>> import custom2
+>>> c1 = custom2.Custom('jim', 'fulton', 42)
+>>> c1.first
 'jim'
->>> n1.last
+>>> c1.last
 'fulton'
->>> n1.number
+>>> c1.number
 42
->>> n1.name()
+>>> c1.name()
 'jim fulton'
->>> n1.first = 'will'
->>> n1.name()
+>>> c1.first = 'will'
+>>> c1.name()
 'will fulton'
->>> n1.last = 'tell'
->>> n1.name()
+>>> c1.last = 'tell'
+>>> c1.name()
 'will tell'
->>> del n1.first
->>> n1.name()
+>>> del c1.first
+>>> c1.name()
 Traceback (most recent call last):
 ...
 AttributeError: first
->>> n1.first
+>>> c1.first
 Traceback (most recent call last):
 ...
 AttributeError: first
->>> n1.first = 'drew'
->>> n1.first
+>>> c1.first = 'drew'
+>>> c1.first
 'drew'
->>> del n1.number
+>>> del c1.number
 Traceback (most recent call last):
 ...
 TypeError: can't delete numeric/char attribute
->>> n1.number=2
->>> n1.number
+>>> c1.number=2
+>>> c1.number
 2
->>> n1.first = 42
->>> n1.name()
+>>> c1.first = 42
+>>> c1.name()
 '42 tell'
->>> n2 = noddy2.Noddy()
->>> n2.name()
+>>> c2 = custom2.Custom()
+>>> c2.name()
 ' '
->>> n2.first
+>>> c2.first
 ''
->>> n2.last
+>>> c2.last
 ''
->>> del n2.first
->>> n2.first
+>>> del c2.first
+>>> c2.first
 Traceback (most recent call last):
 ...
 AttributeError: first
->>> n2.first
+>>> c2.first
 Traceback (most recent call last):
 ...
 AttributeError: first
->>> n2.name()
+>>> c2.name()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 AttributeError: first
->>> n2.number
+>>> c2.number
 0
->>> n3 = noddy2.Noddy('jim', 'fulton', 'waaa')
+>>> n3 = custom2.Custom('jim', 'fulton', 'waaa')
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
-TypeError: an integer is required
->>> del n1
->>> del n2
+TypeError: an integer is required (got type str)
+>>> del c1
+>>> del c2
 
 
-Noddy 3
+Custom 3
 
->>> import noddy3
->>> n1 = noddy3.Noddy('jim', 'fulton', 42)
->>> n1 = noddy3.Noddy('jim', 'fulton', 42)
->>> n1.name()
+>>> import custom3
+>>> c1 = custom3.Custom('jim', 'fulton', 42)
+>>> c1 = custom3.Custom('jim', 'fulton', 42)
+>>> c1.name()
 'jim fulton'
->>> del n1.first
+>>> del c1.first
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: Cannot delete the first attribute
->>> n1.first = 42
+>>> c1.first = 42
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: The first attribute value must be a string
->>> n1.first = 'will'
->>> n1.name()
+>>> c1.first = 'will'
+>>> c1.name()
 'will fulton'
->>> n2 = noddy3.Noddy()
->>> n2 = noddy3.Noddy()
->>> n2 = noddy3.Noddy()
->>> n3 = noddy3.Noddy('jim', 'fulton', 'waaa')
+>>> c2 = custom3.Custom()
+>>> c2 = custom3.Custom()
+>>> c2 = custom3.Custom()
+>>> n3 = custom3.Custom('jim', 'fulton', 'waaa')
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
-TypeError: an integer is required
->>> del n1
->>> del n2
+TypeError: an integer is required (got type str)
+>>> del c1
+>>> del c2
 
-Noddy 4
+Custom 4
 
->>> import noddy4
->>> n1 = noddy4.Noddy('jim', 'fulton', 42)
->>> n1.first
+>>> import custom4
+>>> c1 = custom4.Custom('jim', 'fulton', 42)
+>>> c1.first
 'jim'
->>> n1.last
+>>> c1.last
 'fulton'
->>> n1.number
+>>> c1.number
 42
->>> n1.name()
+>>> c1.name()
 'jim fulton'
->>> n1.first = 'will'
->>> n1.name()
+>>> c1.first = 'will'
+>>> c1.name()
 'will fulton'
->>> n1.last = 'tell'
->>> n1.name()
+>>> c1.last = 'tell'
+>>> c1.name()
 'will tell'
->>> del n1.first
->>> n1.name()
+>>> del c1.first
 Traceback (most recent call last):
 ...
-AttributeError: first
->>> n1.first
-Traceback (most recent call last):
-...
-AttributeError: first
->>> n1.first = 'drew'
->>> n1.first
+TypeError: Cannot delete the first attribute
+>>> c1.name()
+'will tell'
+>>> c1.first = 'drew'
+>>> c1.first
 'drew'
->>> del n1.number
+>>> del c1.number
 Traceback (most recent call last):
 ...
 TypeError: can't delete numeric/char attribute
->>> n1.number=2
->>> n1.number
+>>> c1.number=2
+>>> c1.number
 2
->>> n1.first = 42
->>> n1.name()
-'42 tell'
->>> n2 = noddy4.Noddy()
->>> n2 = noddy4.Noddy()
->>> n2 = noddy4.Noddy()
->>> n2 = noddy4.Noddy()
->>> n2.name()
+>>> c1.first = 42
+Traceback (most recent call last):
+...
+TypeError: The first attribute value must be a string
+>>> c1.name()
+'drew tell'
+>>> c2 = custom4.Custom()
+>>> c2 = custom4.Custom()
+>>> c2 = custom4.Custom()
+>>> c2 = custom4.Custom()
+>>> c2.name()
 ' '
->>> n2.first
+>>> c2.first
 ''
->>> n2.last
+>>> c2.last
 ''
->>> del n2.first
->>> n2.first
-Traceback (most recent call last):
-...
-AttributeError: first
->>> n2.first
-Traceback (most recent call last):
-...
-AttributeError: first
->>> n2.name()
-Traceback (most recent call last):
-  File "<stdin>", line 1, in ?
-AttributeError: first
->>> n2.number
+>>> c2.number
 0
->>> n3 = noddy4.Noddy('jim', 'fulton', 'waaa')
+>>> n3 = custom4.Custom('jim', 'fulton', 'waaa')
 Traceback (most recent call last):
-  File "<stdin>", line 1, in ?
-TypeError: an integer is required
+...
+TypeError: an integer is required (got type str)
 
 
 Test cyclic gc(?)
@@ -183,15 +170,14 @@
 >>> import gc
 >>> gc.disable()
 
->>> x = []
->>> l = [x]
->>> n2.first = l
->>> n2.first
-[[]]
->>> l.append(n2)
->>> del l
->>> del n1
->>> del n2
+>>> class Subclass(custom4.Custom): pass
+...
+>>> s = Subclass()
+>>> s.cycle = [s]
+>>> s.cycle.append(s.cycle)
+>>> x = object()
+>>> s.x = x
+>>> del s
 >>> sys.getrefcount(x)
 3
 >>> ignore = gc.collect()