[Python-checkins] python/dist/src/Doc/ext noddy2.c,NONE,1.1 noddy3.c,NONE,1.1 newtypes.tex,1.21,1.22
dcjim@users.sourceforge.net
dcjim@users.sourceforge.net
Wed, 07 May 2003 12:48:16 -0700
Update of /cvsroot/python/python/dist/src/Doc/ext
In directory sc8-pr-cvs1:/tmp/cvs-serv13294
Modified Files:
newtypes.tex
Added Files:
noddy2.c noddy3.c
Log Message:
Rewrote the basic section of the chapter on defining new types.
Changed the example to show how to create types the new way:
- Use a class new method rather than a new function.
- Use self->ob_type->tp_free in deallocators
- Use attribute descriptors rather than set/getattr methods.
- Make the type usable as a base type.
I split the example into 3 parts:
1. The minimal new type
2. Adding attributes and methods.
3. Finer control over attributes.
It's much simpler to define builtin types. These updates hopefully
show this.
I also made minor wording changes in two other places.
I still need to update xxobject.c
--- NEW FILE: noddy2.c ---
#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(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 PyObject *
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 NULL;
if (first) {
Py_XDECREF(self->first);
Py_INCREF(first);
self->first = first;
}
if (last) {
Py_XDECREF(self->last);
Py_INCREF(last);
self->last = last;
}
Py_INCREF(Py_None);
return Py_None;
}
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 */
};
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;
PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
}
--- NEW FILE: noddy3.c ---
#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(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 PyObject *
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 NULL;
if (first) {
Py_DECREF(self->first);
Py_INCREF(first);
self->first = first;
}
if (last) {
Py_DECREF(self->last);
Py_INCREF(last);
self->last = last;
}
Py_INCREF(Py_None);
return Py_None;
}
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 = {
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 */
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 */
};
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;
PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
}
Index: newtypes.tex
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/newtypes.tex,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -d -r1.21 -r1.22
*** newtypes.tex 8 Jan 2003 03:02:26 -0000 1.21
--- newtypes.tex 7 May 2003 19:48:13 -0000 1.22
***************
*** 3,6 ****
--- 3,7 ----
\sectionauthor{Michael Hudson}{mwh@python.net}
\sectionauthor{Dave Kuhlman}{dkuhlman@rexx.com}
+ \sectionauthor{Jim Fulton}{jim@zope.com}
As mentioned in the last chapter, Python allows the writer of an
***************
*** 39,51 ****
\begin{verbatim}
- static PyTypeObject noddy_NoddyType;
- \end{verbatim}
-
- This names the type object that will be defining further down in the
- file. It can't be defined here because its definition has to refer to
- functions that have not yet been defined, but we need to be able to
- refer to it, hence the declaration.
-
- \begin{verbatim}
typedef struct {
PyObject_HEAD
--- 40,43 ----
***************
*** 74,152 ****
\end{verbatim}
- Next up is:
-
- \begin{verbatim}
- static PyObject*
- noddy_new_noddy(PyObject* self, PyObject* args)
- {
- noddy_NoddyObject* noddy;
-
- if (!PyArg_ParseTuple(args,":new_noddy"))
- return NULL;
-
- noddy = PyObject_New(noddy_NoddyObject, &noddy_NoddyType);
-
- return (PyObject*)noddy;
- }
- \end{verbatim}
-
- This is in fact just a regular module function, as described in the
- last chapter. The reason it gets special mention is that this is
- where we create our Noddy object. Defining \ctype{PyTypeObject}
- structures is all very well, but if there's no way to actually
- \emph{create} one of the wretched things it is not going to do anyone
- much good.
-
- Almost always, you create objects with a call of the form:
-
- \begin{verbatim}
- PyObject_New(<type>, &<type object>);
- \end{verbatim}
-
- This allocates the memory and then initializes the object (sets
- the reference count to one, makes the \member{ob_type} pointer point at
- the right place and maybe some other stuff, depending on build options).
- You \emph{can} do these steps separately if you have some reason to
- --- but at this level we don't bother.
-
- Note that \cfunction{PyObject_New()} is a polymorphic macro rather
- than a real function. The first parameter is the name of the C
- structure that represents an object of our new type, and the return
- value is a pointer to that type. This would be
- \ctype{noddy_NoddyObject} in our example:
-
- \begin{verbatim}
- noddy_NoddyObject *my_noddy;
-
- my_noddy = PyObject_New(noddy_NoddyObject, &noddy_NoddyType);
- \end{verbatim}
-
- We cast the return value to a \ctype{PyObject*} because that's what
- the Python runtime expects. This is safe because of guarantees about
- the layout of structures in the C standard, and is a fairly common C
- programming trick. One could declare \cfunction{noddy_new_noddy} to
- return a \ctype{noddy_NoddyObject*} and then put a cast in the
- definition of \cdata{noddy_methods} further down the file --- it
- doesn't make much difference.
-
- Now a Noddy object doesn't do very much and so doesn't need to
- implement many type methods. One you can't avoid is handling
- deallocation, so we find
-
- \begin{verbatim}
- static void
- noddy_noddy_dealloc(PyObject* self)
- {
- PyObject_Del(self);
- }
- \end{verbatim}
-
- This is so short as to be self explanatory. This function will be
- called when the reference count on a Noddy object reaches \code{0} (or
- it is found as part of an unreachable cycle by the cyclic garbage
- collector). \cfunction{PyObject_Del()} is what you call when you want
- an object to go away. If a Noddy object held references to other
- Python objects, one would decref them here.
-
Moving on, we come to the crunch --- the type object.
--- 66,69 ----
***************
*** 154,176 ****
static PyTypeObject noddy_NoddyType = {
PyObject_HEAD_INIT(NULL)
! 0, /* ob_size */
! "Noddy", /* tp_name */
! sizeof(noddy_NoddyObject), /* tp_basicsize */
! 0, /* tp_itemsize */
! noddy_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 */
};
\end{verbatim}
Now if you go and look up the definition of \ctype{PyTypeObject} in
! \file{object.h} you'll see that it has many, many more fields that the
definition above. The remaining fields will be filled with zeros by
the C compiler, and it's common practice to not specify them
--- 71,117 ----
static PyTypeObject noddy_NoddyType = {
PyObject_HEAD_INIT(NULL)
! 0, /*ob_size*/
! "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 */
! 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 */
};
\end{verbatim}
Now if you go and look up the definition of \ctype{PyTypeObject} in
! \file{object.h} you'll see that it has many more fields that the
definition above. The remaining fields will be filled with zeros by
the C compiler, and it's common practice to not specify them
***************
*** 191,197 ****
as the type of a type object is ``type'', but this isn't strictly
! conforming C and some compilers complain. So instead we fill in the
! \member{ob_type} field of \cdata{noddy_NoddyType} at the earliest
! oppourtunity --- in \cfunction{initnoddy()}.
\begin{verbatim}
--- 132,137 ----
as the type of a type object is ``type'', but this isn't strictly
! conforming C and some compilers complain. Fortunately, this member
! will be filled in for us by \cfunction{PyType_Ready()}.
\begin{verbatim}
***************
*** 205,209 ****
\begin{verbatim}
! "Noddy", /* tp_name */
\end{verbatim}
--- 145,149 ----
\begin{verbatim}
! "noddy.Noddy", /* tp_name */
\end{verbatim}
***************
*** 215,221 ****
Traceback (most recent call last):
File "<stdin>", line 1, in ?
! TypeError: cannot add type "Noddy" to string
\end{verbatim}
\begin{verbatim}
sizeof(noddy_NoddyObject), /* tp_basicsize */
--- 155,166 ----
Traceback (most recent call last):
File "<stdin>", line 1, in ?
! TypeError: cannot add type "noddy.Noddy" to string
\end{verbatim}
+ Note that the name is a dotted name that includes both the module name
+ and the name of the type within the module. The module in this case is
+ \module{noddy} and the type is \class{Noddy}, so we set the type name
+ to \class{noddy.Noddy}.
+
\begin{verbatim}
sizeof(noddy_NoddyObject), /* tp_basicsize */
***************
*** 232,266 ****
Ignore this for now.
Now we get into the type methods, the things that make your objects
! different from the others. Of course, the Noddy object doesn't
! implement many of these, but as mentioned above you have to implement
! the deallocation function.
\begin{verbatim}
! noddy_noddy_dealloc, /* tp_dealloc */
\end{verbatim}
! From here, all the type methods are \NULL, so we'll go over them later
! --- that's for the next section!
! Everything else in the file should be familiar, except for this line
in \cfunction{initnoddy}:
\begin{verbatim}
! noddy_NoddyType.ob_type = &PyType_Type;
\end{verbatim}
! This was alluded to above --- the \cdata{noddy_NoddyType} object should
! have type ``type'', but \code{\&PyType_Type} is not constant and so
! can't be used in its initializer. To work around this, we patch it up
! in the module initialization.
That's it! All that remains is to build it; put the above code in a
! file called \file{noddymodule.c} and
\begin{verbatim}
from distutils.core import setup, Extension
setup(name="noddy", version="1.0",
! ext_modules=[Extension("noddy", ["noddymodule.c"])])
\end{verbatim}
--- 177,244 ----
Ignore this for now.
+ Skipping a number of type methods that we don't provide, we set the
+ class flags to \constant{Py_TPFLAGS_DEFAULT}.
+
+ \begin{verbatim}
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ \end{verbatim}
+
+ All types should include this constant in their flags. It enables all
+ of the members defined by the current version of Python.
+
+ We provide a doc string for the type in \member{tp_doc}.
+
+ \begin{verbatim}
+ "Noddy objects", /* tp_doc */
+ \end{verbatim}
+
Now we get into the type methods, the things that make your objects
! different from the others. We aren't going to implement any of these
! in this version of the module. We'll expand this example later to
! have more interesting behavior.
!
! For now, all we want to be able to do is to create new \class{Noddy}
! objects. To enable object creation, we have to provide a
! \member{tp_new} implementation. In this case, we can just use the
! default implementation provided by the API function
! \cfunction{PyType_GenericNew}.
\begin{verbatim}
! PyType_GenericNew, /* tp_new */
\end{verbatim}
! All the other type methods are \NULL, so we'll go over them later
! --- that's for a later section!
! Everything else in the file should be familiar, except for some code
in \cfunction{initnoddy}:
\begin{verbatim}
! if (PyType_Ready(&noddy_NoddyType) < 0)
! return;
\end{verbatim}
! This initializes the \class{Noddy} type, filing in a number of
! members, including \member{ob_type} that we initially set to \NULL.
!
! \begin{verbatim}
! PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);
! \end{verbatim}
!
! This adds the type to the module dictionary. This allows us to create
! \class{Noddy} instances by calling the \class{Noddy} class:
!
! \begin{verbatim}
! import noddy
! mynoddy = noddy.Noddy()
! \end{verbatim}
That's it! All that remains is to build it; put the above code in a
! file called \file{noddy.c} and
\begin{verbatim}
from distutils.core import setup, Extension
setup(name="noddy", version="1.0",
! ext_modules=[Extension("noddy", ["noddy.c"])])
\end{verbatim}
***************
*** 277,280 ****
--- 255,676 ----
That wasn't so hard, was it?
+ Of course, the current Noddy type is pretty uninteresting. It has no
+ data and doesn't do anything. It can't even be subclasses.
+
+ \subsection{Adding data and methods to the Basic example}
+
+ Let's expend the basic example to add some data and methods. Let's
+ also make the type usable as a base class. We'll create
+ a new module, \module{noddy2} that adds these capabilities:
+
+ \verbatiminput{noddy2.c}
+
+ This version of the module has a number of changes.
+
+ We've added an extra include:
+
+ \begin{verbatim}
+ #include "structmember.h"
+ \end{verbatim}
+
+ This include provides declarations that we use to handle attributes,
+ as described a bit later.
+
+ The name of the \class{Noddy} object structure has been shortened to
+ \class{Noddy}. The type object name has been shortened to
+ \class{NoddyType}.
+
+ The \class{Noddy} type now has three data attributes, \var{first},
+ \var{last}, and \var{number}. The \var{first} and \var{last}
+ variables are Python strings containing first and last names. The
+ \var{number} attribute is an integer.
+
+ The object structure is updated accordingly:
+
+ \begin{verbatim}
+ typedef struct {
+ PyObject_HEAD
+ PyObject *first;
+ PyObject *last;
+ int number;
+ } Noddy;
+ \end{verbatim}
+
+ Because we now have data to manage, we have to be more careful about
+ object allocation and deallocation. At a minimum, we need a
+ deallocation method:
+
+ \begin{verbatim}
+ static void
+ Noddy_dealloc(Noddy* self)
+ {
+ Py_XDECREF(self->first);
+ Py_XDECREF(self->last);
+ self->ob_type->tp_free(self);
+ }
+ \end{verbatim}
+
+ which is assigned to the \member{tp_dealloc} member:
+
+ \begin{verbatim}
+ (destructor)Noddy_dealloc, /*tp_dealloc*/
+ \end{verbatim}
+
+ This method decrements the reference counts of the two Python
+ attributes. We use \cfunction{Py_XDECREF} here because the
+ \member{first} and \member{last} members could be \NULL. It then
+ calls the \member{tp_free} member of the object's type to free the
+ object's memory. Note that the object's type might not be
+ \class{NoddyType}, because the object may be an instance of a
+ subclass.
+
+ We want to make sure that the first and last names are initialized to
+ empty strings, so we provide a new method:
+
+ \begin{verbatim}
+ 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;
+ }
+ \end{verbatim}
+
+ and install it in the \member{tp_new} member:
+
+ \begin{verbatim}
+ Noddy_new, /* tp_new */
+ \end{verbatim}
+
+ The new member is responsible for creating (as opposed to
+ initializing) objects of the type. It is exposed in Python as the
+ \method{__new__} method. See the paper titled ``Unifying types and
+ classes in Python'' for a detailed discussion of the \method{__new__}
+ method. One reason to implement a new method is to assure the initial
+ values of instance variables. In this case, we use the new method to
+ make sure that the initial values of the members \member{first} and
+ \member{last} are not \NULL. If we didn't care whether the initial
+ values were \NULL, we could have used \cfunction{PyType_GenericNew} as
+ our new method, as we did before. \cfunction{PyType_GenericNew}
+ initializes all of the instance variable members to NULLs.
+
+ The new method is a static method that is passed the type being
+ instantiated and any arguments passed when the type was called,
+ and that returns the new object created. New methods always accept
+ positional and keyword arguments, but they often ignore the arguments,
+ leaving the argument handling to initializer methods. Note that if the
+ type supports subclassing, the type passed may not be the type being
+ defined. The new method calls the tp_alloc slot to allocate memory.
+ We don't fill the \member{tp_alloc} slot ourselves. Rather
+ \cfunction{PyType_Ready()} fills it for us by inheriting it from our
+ base class, which is \class{object} by default. Most types use the
+ default allocation.
+
+ We provide an initialization function:
+
+ \begin{verbatim}
+ static PyObject *
+ 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 NULL;
+
+ if (first) {
+ Py_XDECREF(self->first);
+ Py_INCREF(first);
+ self->first = first;
+ }
+
+ if (last) {
+ Py_XDECREF(self->last);
+ Py_INCREF(last);
+ self->last = last;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ \end{verbatim}
+
+ by filling the \member{tp_init} slot.
+
+ \begin{verbatim}
+ (initproc)Noddy_init, /* tp_init */
+ \end{verbatim}
+
+ The \member{tp_init} slot is exposed in Python as the
+ \method{__init__} method. It is used to initialize an object after
+ it's created. Unlike the new method, we can't guarantee that the
+ initializer is called. The initializer isn't called when unpickling
+ objects and it can be overridden. Our initializer accepts arguments
+ to provide initial values for our instance. Initializers always accept
+ positional and keyword arguments.
+
+ We want to want to expose our instance variables as attributes. There
+ are a number of ways to do that. The simplest way is to define member
+ definitions:
+
+ \begin{verbatim}
+ 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 */
+ };
+ \end{verbatim}
+
+ and put the definitions in the \member{tp_members} slot:
+
+ \begin{verbatim}
+ Noddy_members, /* tp_members */
+ \end{verbatim}
+
+ Each member definition has a member name, type, offset, access flags
+ and documentation string. See the ``Generic Attribute Management''
+ section below for details.
+
+ A disadvantage of this approach is that it doesn't provide a way to
+ restrict the types of objects that can be assigned to the Python
+ attributes. We expect the first and last names to be strings, but any
+ Python objects can be assigned. Further, the attributes can be
+ deleted, setting the C pointers to \NULL. Even though we can make
+ sure the members are initialized to non-\NULL values, the members can
+ be set to \NULL if the attributes are deleted.
+
+ We define a single method, \method{name}, that outputs the objects
+ name as the concatenation of the first and last names.
+
+ \begin{verbatim}
+ 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;
+ }
+ \end{verbatim}
+
+ The method is implemented as a C function that takes a \class{Noddy} (or
+ \class{Noddy} subclass) instance as the first argument. Methods
+ always take an instance as the first argument. Methods often take
+ positional and keyword arguments as well, but in this cased we don't
+ take any and don't need to accept a positional argument tuple or
+ keyword argument dictionary. This method is equivalent to the Python
+ method:
+
+ \begin{verbatim}
+ def name(self):
+ return "%s %s" % (self.first, self.last)
+ \end{verbatim}
+
+ Note that we have to check for the possibility that our \member{first}
+ and \member{last} members are \NULL. This is because they can be
+ deleted, in which case they are set to \NULL. It would be better to
+ prevent deletion of these attributes and to restrict the attribute
+ values to be strings. We'll see how to do that in the next section.
+
+ Now that we've defined the method, we need to create an array of
+ method definitions:
+
+ \begin{verbatim}
+ static PyMethodDef Noddy_methods[] = {
+ {"name", (PyCFunction)Noddy_name, METH_NOARGS,
+ "Return the name, combining the first and last name"
+ },
+ {NULL} /* Sentinel */
+ };
+ \end{verbatim}
+
+ and assign them to the \member{tp_methods} slot:
+
+ \begin{verbatim}
+ Noddy_methods, /* tp_methods */
+ \end{verbatim}
+
+ Note that used the \constant{METH_NOARGS} flag to indicate that the
+ method is passed no arguments.
+
+ Finally, we'll make our type usable as a base class. We've written
+ our methods carefully so far so that they don't make any assumptions
+ about the type of the object being created or used, so all we need to
+ do is to add the \constant{Py_TPFLAGS_BASETYPE} to our class flag
+ definition:
+
+ \begin{verbatim}
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ \end{verbatim}
+
+ We rename \cfunction{initnoddy} to \cfunction{initnoddy2}
+ and update the module name passed to \cfunction{Py_InitModule3}.
+
+ Finally, we update our \file{setup.py} file to build the new module:
+
+ \begin{verbatim}
+ from distutils.core import setup, Extension
+ setup(name="noddy", version="1.0",
+ ext_modules=[
+ Extension("noddy", ["noddy.c"]),
+ Extension("noddy2", ["noddy2.c"]),
+ ])
+ \end{verbatim}
+
+ \subsection{Providing finer control over data attributes}
+
+ In this section, we'll provide finer control over how the
+ \member{first} and \member{last} attributes are set in the
+ \class{Noddy} example. In the previous version of our module, the
+ instance variables \member{first} and \member{last} could be set to
+ non-string values or even deleted. We want to make sure that these
+ attributes always contain strings.
+
+ \verbatiminput{noddy3.c}
+
+ To provide greater control, over the \member{first} and \member{last}
+ attributes, we'll use custom getter and setter functions. Here are
+ the functions for getting and setting the \member{first} attribute:
+
+ \begin{verbatim}
+ 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;
+ }
+ \end{verbatim}
+
+ The getter function is passed a \class{Noddy} object and a
+ ``closure'', which is void pointer. In this case, the closure is
+ ignored. (The closure supports an advanced usage in which definition
+ data is passed to the getter and setter. This could, for example, be
+ used to allow a single set of getter and setter functions that decide
+ the attribute to get or set based on data in the closure.)
+
+ The setter function is passed the \class{Noddy} object, the new value,
+ and the closure. The new value may be \NULL, in which case the
+ attribute is being deleted. In our setter, we raise an error if the
+ attribute is deleted or if the attribute value is not a string.
+
+ We create an array of \ctype{PyGetSetDef} structures:
+
+ \begin{verbatim}
+ 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 */
+ };
+ \end{verbatim}
+
+ and register it in the \member{tp_getset} slot:
+
+ \begin{verbatim}
+ Noddy_getseters, /* tp_getset */
+ \end{verbatim}
+
+ to register out attribute getters and setters.
+
+ The last item in a \ctype{PyGetSetDef} structure is the closure
+ mentioned above. In this case, we aren't using the closure, so we just
+ pass \NULL.
+
+ We also remove the member definitions for these attributes:
+
+ \begin{verbatim}
+ static PyMemberDef Noddy_members[] = {
+ {"number", T_INT, offsetof(Noddy, number), 0,
+ "noddy number"},
+ {NULL} /* Sentinel */
+ };
+ \end{verbatim}
+
+ With these changes, we can assure that the \member{first} and
+ \member{last} members are never NULL so we can remove checks for \NULL
+ values in almost all cases. This means that most of the
+ \cfunction{Py_XDECREF} calls can be converted to \cfunction{Py_DECREF}
+ calls. The only place we can't change these calls is in the
+ deallocator, where there is the possibility that the initialization of
+ these members failed in the constructor.
+
+ We also rename the module initialization function and module name in
+ the initialization function, as we did before, and we add an extra
+ definition to the \file{setup.py} file.
\section{Type Methods
***************
*** 354,358 ****
{
free(obj->obj_UnderlyingDatatypePtr);
! PyObject_DEL(obj);
}
\end{verbatim}
--- 750,754 ----
{
free(obj->obj_UnderlyingDatatypePtr);
! obj->ob_type->tp_free(self);
}
\end{verbatim}
***************
*** 397,401 ****
Py_DECREF(self->my_callback);
}
! PyObject_DEL(obj);
}
\end{verbatim}
--- 793,797 ----
Py_DECREF(self->my_callback);
}
! obj->ob_type->tp_free(self);
}
\end{verbatim}
***************
*** 717,721 ****
The \member{tp_compare} handler is called when comparisons are needed
! are the object does not implement the specific rich comparison method
which matches the requested comparison. (It is always used if defined
and the \cfunction{PyObject_Compare()} or \cfunction{PyObject_Cmp()}
--- 1113,1117 ----
The \member{tp_compare} handler is called when comparisons are needed
! and the object does not implement the specific rich comparison method
which matches the requested comparison. (It is always used if defined
and the \cfunction{PyObject_Compare()} or \cfunction{PyObject_Cmp()}
***************
*** 773,780 ****
which depend on several handler routines from the type implementation,
the older protocols have been defined as optional blocks of handlers
! referenced by the type object, while newer protocols have been added
! using additional slots in the main type object, with a flag bit being
! set to indicate that the slots are present. (The flag bit does not
! indicate that the slot values are non-\NULL.)
\begin{verbatim}
--- 1169,1178 ----
which depend on several handler routines from the type implementation,
the older protocols have been defined as optional blocks of handlers
! referenced by the type object. For newer protocols there are
! additional slots in the main type object, with a flag bit being set to
! indicate that the slots are present and should be checked by the
! interpreter. (The flag bit does not indicate that the slot values are
! non-\NULL. The flag may be set to indicate the presense of a slot,
! but a slot may still be unfilled.)
\begin{verbatim}