<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <link rel="STYLESHEET" href="ext.css" type='text/css' /> <link rel="SHORTCUT ICON" href="../icons/pyfav.gif" /> <link rel='start' href='../index.html' title='Python Documentation Index' /> <link rel="first" href="ext.html" title='Extending and Embedding the Python Interpreter' /> <link rel='contents' href='contents.html' title="Contents" /> <link rel='last' href='about.html' title='About this document...' /> <link rel='help' href='about.html' title='About this document...' /> <LINK rel="next" href="node23.html"> <LINK rel="prev" href="dnt-basics.html"> <LINK rel="parent" href="dnt-basics.html"> <LINK rel="next" href="node23.html"> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta name='aesop' content='information' /> <META name="description" content="Adding data and methods to the Basic example"> <META name="keywords" content="ext"> <META name="resource-type" content="document"> <META name="distribution" content="global"> <title>2.1.1 Adding data and methods to the Basic example</title> </head> <body> <DIV CLASS="navigation"> <div id='top-navigation-panel'> <table align="center" width="100%" cellpadding="0" cellspacing="2"> <tr> <td class='online-navigation'><a rel="prev" title="2.1 The Basics" href="dnt-basics.html"><img src='../icons/previous.png' border='0' height='32' alt='Previous Page' width='32' /></A></td> <td class='online-navigation'><a rel="parent" title="2.1 The Basics" href="dnt-basics.html"><img src='../icons/up.png' border='0' height='32' alt='Up One Level' width='32' /></A></td> <td class='online-navigation'><a rel="next" title="2.1.2 Providing finer control" href="node23.html"><img src='../icons/next.png' border='0' height='32' alt='Next Page' width='32' /></A></td> <td align="center" width="100%">Extending and Embedding the Python Interpreter</td> <td class='online-navigation'><a rel="contents" title="Table of Contents" href="contents.html"><img src='../icons/contents.png' border='0' height='32' alt='Contents' width='32' /></A></td> <td class='online-navigation'><img src='../icons/blank.png' border='0' height='32' alt='' width='32' /></td> <td class='online-navigation'><img src='../icons/blank.png' border='0' height='32' alt='' width='32' /></td> </tr></table> <div class='online-navigation'> <b class="navlabel">Previous:</b> <a class="sectref" rel="prev" href="dnt-basics.html">2.1 The Basics</A> <b class="navlabel">Up:</b> <a class="sectref" rel="parent" href="dnt-basics.html">2.1 The Basics</A> <b class="navlabel">Next:</b> <a class="sectref" rel="next" href="node23.html">2.1.2 Providing finer control</A> </div> <hr /></div> </DIV> <!--End of Navigation Panel--> <H2><A NAME="SECTION004110000000000000000"> 2.1.1 Adding data and methods to the Basic example</A> </H2> <P> 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, <tt class="module">noddy2</tt> that adds these capabilities: <P> <div class="verbatim"> <pre>#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); } </pre> <div class="footer"> <a href="noddy2.txt" type="text/plain">Download as text (original file name: <span class="file">noddy2.c</span>).</a> </div></div> <P> This version of the module has a number of changes. <P> We've added an extra include: <P> <div class="verbatim"><pre> #include "structmember.h" </pre></div> <P> This include provides declarations that we use to handle attributes, as described a bit later. <P> The name of the <tt class="class">Noddy</tt> object structure has been shortened to <tt class="class">Noddy</tt>. The type object name has been shortened to <tt class="class">NoddyType</tt>. <P> The <tt class="class">Noddy</tt> type now has three data attributes, <var>first</var>, <var>last</var>, and <var>number</var>. The <var>first</var> and <var>last</var> variables are Python strings containing first and last names. The <var>number</var> attribute is an integer. <P> The object structure is updated accordingly: <P> <div class="verbatim"><pre> typedef struct { PyObject_HEAD PyObject *first; PyObject *last; int number; } Noddy; </pre></div> <P> 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: <P> <div class="verbatim"><pre> static void Noddy_dealloc(Noddy* self) { Py_XDECREF(self->first); Py_XDECREF(self->last); self->ob_type->tp_free((PyObject*)self); } </pre></div> <P> which is assigned to the <tt class="member">tp_dealloc</tt> member: <P> <div class="verbatim"><pre> (destructor)Noddy_dealloc, /*tp_dealloc*/ </pre></div> <P> This method decrements the reference counts of the two Python attributes. We use <tt class="cfunction">Py_XDECREF()</tt> here because the <tt class="member">first</tt> and <tt class="member">last</tt> members could be <tt class="constant">NULL</tt>. It then calls the <tt class="member">tp_free</tt> member of the object's type to free the object's memory. Note that the object's type might not be <tt class="class">NoddyType</tt>, because the object may be an instance of a subclass. <P> We want to make sure that the first and last names are initialized to empty strings, so we provide a new method: <P> <div class="verbatim"><pre> 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; } </pre></div> <P> and install it in the <tt class="member">tp_new</tt> member: <P> <div class="verbatim"><pre> Noddy_new, /* tp_new */ </pre></div> <P> The new member is responsible for creating (as opposed to initializing) objects of the type. It is exposed in Python as the <tt class="method">__new__()</tt> method. See the paper titled ``Unifying types and classes in Python'' for a detailed discussion of the <tt class="method">__new__()</tt> 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 <tt class="member">first</tt> and <tt class="member">last</tt> are not <tt class="constant">NULL</tt>. If we didn't care whether the initial values were <tt class="constant">NULL</tt>, we could have used <tt class="cfunction">PyType_GenericNew()</tt> as our new method, as we did before. <tt class="cfunction">PyType_GenericNew()</tt> initializes all of the instance variable members to NULLs. <P> 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 <tt class="member">tp_alloc</tt> slot ourselves. Rather <tt class="cfunction">PyType_Ready()</tt> fills it for us by inheriting it from our base class, which is <tt class="class">object</tt> by default. Most types use the default allocation. <P> We provide an initialization function: <P> <div class="verbatim"><pre> 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; } </pre></div> <P> by filling the <tt class="member">tp_init</tt> slot. <P> <div class="verbatim"><pre> (initproc)Noddy_init, /* tp_init */ </pre></div> <P> The <tt class="member">tp_init</tt> slot is exposed in Python as the <tt class="method">__init__()</tt> 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. <P> 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: <P> <div class="verbatim"><pre> 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 */ }; </pre></div> <P> and put the definitions in the <tt class="member">tp_members</tt> slot: <P> <div class="verbatim"><pre> Noddy_members, /* tp_members */ </pre></div> <P> Each member definition has a member name, type, offset, access flags and documentation string. See the ``Generic Attribute Management'' section below for details. <P> 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 <tt class="constant">NULL</tt>. Even though we can make sure the members are initialized to non-<tt class="constant">NULL</tt> values, the members can be set to <tt class="constant">NULL</tt> if the attributes are deleted. <P> We define a single method, <tt class="method">name</tt>, that outputs the objects name as the concatenation of the first and last names. <P> <div class="verbatim"><pre> 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; } </pre></div> <P> The method is implemented as a C function that takes a <tt class="class">Noddy</tt> (or <tt class="class">Noddy</tt> 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: <P> <div class="verbatim"><pre> def name(self): return "%s %s" % (self.first, self.last) </pre></div> <P> Note that we have to check for the possibility that our <tt class="member">first</tt> and <tt class="member">last</tt> members are <tt class="constant">NULL</tt>. This is because they can be deleted, in which case they are set to <tt class="constant">NULL</tt>. 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. <P> Now that we've defined the method, we need to create an array of method definitions: <P> <div class="verbatim"><pre> static PyMethodDef Noddy_methods[] = { {"name", (PyCFunction)Noddy_name, METH_NOARGS, "Return the name, combining the first and last name" }, {NULL} /* Sentinel */ }; </pre></div> <P> and assign them to the <tt class="member">tp_methods</tt> slot: <P> <div class="verbatim"><pre> Noddy_methods, /* tp_methods */ </pre></div> <P> Note that we used the <tt class="constant">METH_NOARGS</tt> flag to indicate that the method is passed no arguments. <P> 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 <tt class="constant">Py_TPFLAGS_BASETYPE</tt> to our class flag definition: <P> <div class="verbatim"><pre> Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ </pre></div> <P> We rename <tt class="cfunction">initnoddy()</tt> to <tt class="cfunction">initnoddy2()</tt> and update the module name passed to <tt class="cfunction">Py_InitModule3()</tt>. <P> Finally, we update our <span class="file">setup.py</span> file to build the new module: <P> <div class="verbatim"><pre> from distutils.core import setup, Extension setup(name="noddy", version="1.0", ext_modules=[ Extension("noddy", ["noddy.c"]), Extension("noddy2", ["noddy2.c"]), ]) </pre></div> <P> <DIV CLASS="navigation"> <div class='online-navigation'><hr /> <table align="center" width="100%" cellpadding="0" cellspacing="2"> <tr> <td class='online-navigation'><a rel="prev" title="2.1 The Basics" rel="prev" title="2.1 The Basics" href="dnt-basics.html"><img src='../icons/previous.png' border='0' height='32' alt='Previous Page' width='32' /></A></td> <td class='online-navigation'><a rel="parent" title="2.1 The Basics" rel="parent" title="2.1 The Basics" href="dnt-basics.html"><img src='../icons/up.png' border='0' height='32' alt='Up One Level' width='32' /></A></td> <td class='online-navigation'><a rel="next" title="2.1.2 Providing finer control" rel="next" title="2.1.2 Providing finer control" href="node23.html"><img src='../icons/next.png' border='0' height='32' alt='Next Page' width='32' /></A></td> <td align="center" width="100%">Extending and Embedding the Python Interpreter</td> <td class='online-navigation'><a rel="contents" title="Table of Contents" rel="contents" title="Table of Contents" href="contents.html"><img src='../icons/contents.png' border='0' height='32' alt='Contents' width='32' /></A></td> <td class='online-navigation'><img src='../icons/blank.png' border='0' height='32' alt='' width='32' /></td> <td class='online-navigation'><img src='../icons/blank.png' border='0' height='32' alt='' width='32' /></td> </tr></table> <div class='online-navigation'> <b class="navlabel">Previous:</b> <a class="sectref" rel="prev" href="dnt-basics.html">2.1 The Basics</A> <b class="navlabel">Up:</b> <a class="sectref" rel="parent" href="dnt-basics.html">2.1 The Basics</A> <b class="navlabel">Next:</b> <a class="sectref" rel="next" href="node23.html">2.1.2 Providing finer control</A> </div> </div> <hr /> <span class="release-info">Release 2.3.4, documentation updated on May 20, 2004.</span> </DIV> <!--End of Navigation Panel--> <ADDRESS> See <i><a href="about.html">About this document...</a></i> for information on suggesting changes. </ADDRESS> </BODY> </HTML>