 |
 |
 |
Extending and Embedding the Python Interpreter |
 |
 |
 |
It is possible to create new extension types that are derived from existing
types. It is easiest to inherit from the built in types, since an extension
can easily use the PyTypeObject it needs. It can be difficult to
share these PyTypeObject structures between extension modules.
In this example we will create a Shoddy type that inherits from
the builtin list type. The new type will be completely compatible
with regular lists, but will have an additional increment() method
that increases an internal counter.
>>> import shoddy
>>> s = shoddy.Shoddy(range(3))
>>> s.extend(s)
>>> print len(s)
6
>>> print s.increment()
1
>>> print s.increment()
2
#include <Python.h>
typedef struct {
PyListObject list;
int state;
} Shoddy;
static PyObject *
Shoddy_increment(Shoddy *self, PyObject *unused)
{
self->state++;
return PyInt_FromLong(self->state);
}
static PyMethodDef Shoddy_methods[] = {
{"increment", (PyCFunction)Shoddy_increment, METH_NOARGS,
PyDoc_STR("increment state counter")},
{NULL, NULL},
};
static int
Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds)
{
if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
}
static PyTypeObject ShoddyType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"shoddy.Shoddy", /* tp_name */
sizeof(Shoddy), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Shoddy_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Shoddy_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyMODINIT_FUNC
initshoddy(void)
{
PyObject *m;
ShoddyType.tp_base = &PyList_Type;
if (PyType_Ready(&ShoddyType) < 0)
return;
m = Py_InitModule3("shoddy", NULL, "Shoddy module");
if (m == NULL)
return;
Py_INCREF(&ShoddyType);
PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType);
}
As you can see, the source code closely resembles the Noddy examples in previous
sections. We will break down the main differences between them.
typedef struct {
PyListObject list;
int state;
} Shoddy;
The primary difference for derived type objects is that the base type's
object structure must be the first value. The base type will already
include the PyObject_HEAD at the beginning of its structure.
When a Python object is a Shoddy instance, its PyObject* pointer
can be safely cast to both PyListObject* and Shoddy*.
static int
Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds)
{
if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
}
In the __init__ method for our type, we can see how to call through
to the __init__ method of the base type.
This pattern is important when writing a type with custom new and
dealloc methods. The new method should not actually create the
memory for the object with tp_alloc, that will be handled by
the base class when calling its tp_new.
When filling out the PyTypeObject for the Shoddy type,
you see a slot for tp_base. Due to cross platform compiler
issues, you can't fill that field directly with the PyList_Type;
it can be done later in the module's init function.
PyMODINIT_FUNC
initshoddy(void)
{
PyObject *m;
ShoddyType.tp_base = &PyList_Type;
if (PyType_Ready(&ShoddyType) < 0)
return;
m = Py_InitModule3("shoddy", NULL, "Shoddy module");
if (m == NULL)
return;
Py_INCREF(&ShoddyType);
PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType);
}
Before calling PyType_Ready, the type structure must have the
tp_base slot filled in. When we are deriving a new type, it is
not necessary to fill out the tp_alloc slot with
PyType_GenericNew - the allocate function from the base type
will be inherited.
After that, calling PyType_Ready and adding the type object
to the module is the same as with the basic Noddy examples.
Release 2.5.2, documentation updated on 21th February, 2008.
See About this document... for information on suggesting changes.
|