<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN">
<link rel=
"STYLESHEET" href=
"ext.css" type='text/css'
/>
<link rel=
"SHORTCUT ICON" href=
"../icons/pyfav.png" type=
"image/png" />
<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=
"node24.html" />
<link rel=
"prev" href=
"node22.html" />
<link rel=
"parent" href=
"dnt-basics.html" />
<link rel=
"next" href=
"node24.html" />
<meta name='aesop' content='information'
/>
<title>2.1.2 Providing finer control over data attributes
</title>
<div id='top-navigation-panel' xml:id='top-navigation-panel'
>
<table align=
"center" width=
"100%" cellpadding=
"0" cellspacing=
"2">
<td class='online-navigation'
><a rel=
"prev" title=
"2.1.1 Adding data and"
href=
"node22.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.3 Supporting cyclic garbage"
href=
"node24.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>
<div class='online-navigation'
>
<b class=
"navlabel">Previous:
</b>
<a class=
"sectref" rel=
"prev" href=
"node22.html">2.1.1 Adding data and
</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=
"node24.html">2.1.3 Supporting cyclic garbage
</A>
<!--End of Navigation Panel-->
<H2><A NAME=
"SECTION004120000000000000000">
2.1.2 Providing finer control over data attributes
</A>
In this section, we'll provide finer control over how the
<tt class=
"member">first
</tt> and
<tt class=
"member">last
</tt> attributes are set in the
<tt class=
"class">Noddy
</tt> example. In the previous version of our module, the
instance variables
<tt class=
"member">first
</tt> and
<tt class=
"member">last
</tt> could be set to
non-string values or even deleted. We want to make sure that these
attributes always contain strings.
<pre>#include
<Python.h
>
#include
"structmember.h"
Noddy_dealloc(Noddy* self)
Py_XDECREF(self-
>first);
Py_XDECREF(self-
>last);
self-
>ob_type-
>tp_free((PyObject*)self);
Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (Noddy *)type-
>tp_alloc(type,
0);
self-
>first = PyString_FromString(
"");
if (self-
>first == NULL)
self-
>last = PyString_FromString(
"");
if (self-
>last == NULL)
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,
static PyMemberDef Noddy_members[] = {
{
"number", T_INT, offsetof(Noddy, number),
0,
Noddy_getfirst(Noddy *self, void *closure)
Py_INCREF(self-
>first);
Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
PyErr_SetString(PyExc_TypeError,
"Cannot delete the first attribute");
if (! PyString_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be a string");
Py_DECREF(self-
>first);
Noddy_getlast(Noddy *self, void *closure)
Py_INCREF(self-
>last);
Noddy_setlast(Noddy *self, PyObject *value, void *closure)
PyErr_SetString(PyExc_TypeError,
"Cannot delete the last attribute");
if (! PyString_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The last attribute value must be a string");
Py_DECREF(self-
>last);
static PyGetSetDef Noddy_getseters[] = {
(getter)Noddy_getfirst, (setter)Noddy_setfirst,
(getter)Noddy_getlast, (setter)Noddy_setlast,
static PyObject *format = NULL;
format = PyString_FromString(
"%s %s");
args = Py_BuildValue(
"OO", self-
>first, self-
>last);
result = PyString_Format(format, args);
static PyMethodDef Noddy_methods[] = {
{
"name", (PyCFunction)Noddy_name, METH_NOARGS,
"Return the name, combining the first and last name"
static PyTypeObject NoddyType = {
"noddy.Noddy", /*tp_name*/
sizeof(Noddy), /*tp_basicsize*/
(destructor)Noddy_dealloc, /*tp_dealloc*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Noddy objects", /* tp_doc */
0, /* tp_weaklistoffset */
Noddy_methods, /* tp_methods */
Noddy_members, /* tp_members */
Noddy_getseters, /* tp_getset */
(initproc)Noddy_init, /* tp_init */
static PyMethodDef module_methods[] = {
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
if (PyType_Ready(
&NoddyType)
< 0)
m = Py_InitModule3(
"noddy3", module_methods,
"Example module that creates an extension type.");
Py_INCREF(
&NoddyType);
PyModule_AddObject(m,
"Noddy", (PyObject *)
&NoddyType);
<a href=
"noddy3.txt" type=
"text/plain">Download as text (original file name:
<span class=
"file">noddy3.c
</span>).
</a>
To provide greater control, over the
<tt class=
"member">first
</tt> and
<tt class=
"member">last
</tt>
attributes, we'll use custom getter and setter functions. Here are
the functions for getting and setting the
<tt class=
"member">first
</tt> attribute:
<div class=
"verbatim"><pre>
Noddy_getfirst(Noddy *self, void *closure)
Py_INCREF(self-
>first);
Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
PyErr_SetString(PyExc_TypeError,
"Cannot delete the first attribute");
if (! PyString_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be a string");
Py_DECREF(self-
>first);
The getter function is passed a
<tt class=
"class">Noddy
</tt> 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
<tt class=
"class">Noddy
</tt> object, the new value,
and the closure. The new value may be
<tt class=
"constant">NULL
</tt>, 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
<tt class=
"ctype">PyGetSetDef
</tt> structures:
<div class=
"verbatim"><pre>
static PyGetSetDef Noddy_getseters[] = {
(getter)Noddy_getfirst, (setter)Noddy_setfirst,
(getter)Noddy_getlast, (setter)Noddy_setlast,
and register it in the
<tt class=
"member">tp_getset
</tt> slot:
<div class=
"verbatim"><pre>
Noddy_getseters, /* tp_getset */
to register out attribute getters and setters.
The last item in a
<tt class=
"ctype">PyGetSetDef
</tt> structure is the closure
mentioned above. In this case, we aren't using the closure, so we just
pass
<tt class=
"constant">NULL
</tt>.
We also remove the member definitions for these attributes:
<div class=
"verbatim"><pre>
static PyMemberDef Noddy_members[] = {
{
"number", T_INT, offsetof(Noddy, number),
0,
We also need to update the
<tt class=
"member">tp_init
</tt> handler to only allow
strings
<A NAME=
"tex2html7"
HREF=
"#foot1157"><SUP>2.3</SUP></A> to be passed:
<div class=
"verbatim"><pre>
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,
With these changes, we can assure that the
<tt class=
"member">first
</tt> and
<tt class=
"member">last
</tt> members are never
<tt class=
"constant">NULL
</tt> so we can remove checks for
<tt class=
"constant">NULL
</tt>
values in almost all cases. This means that most of the
<tt class=
"cfunction">Py_XDECREF()
</tt> calls can be converted to
<tt class=
"cfunction">Py_DECREF()
</tt>
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
<span class=
"file">setup.py
</span> file.
<BR><HR><H4>Footnotes
</H4>
<DT><A NAME=
"foot1157">...
HREF=
"node23.html#tex2html7"><SUP>2.3</SUP></A></DT>
<DD>We now know that the first and last members are strings,
so perhaps we could be less careful about decrementing their
reference counts, however, we accept instances of string subclasses.
Even though deallocating normal strings won't call back into our
objects, we can't guarantee that deallocating an instance of a string
subclass won't. call back into out objects.
<div class='online-navigation'
>
<table align=
"center" width=
"100%" cellpadding=
"0" cellspacing=
"2">
<td class='online-navigation'
><a rel=
"prev" title=
"2.1.1 Adding data and"
href=
"node22.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.3 Supporting cyclic garbage"
href=
"node24.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>
<div class='online-navigation'
>
<b class=
"navlabel">Previous:
</b>
<a class=
"sectref" rel=
"prev" href=
"node22.html">2.1.1 Adding data and
</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=
"node24.html">2.1.3 Supporting cyclic garbage
</A>
<span class=
"release-info">Release
2.4.2, documentation updated on
28 September
2005.
</span>
<!--End of Navigation Panel-->
See
<i><a href=
"about.html">About this document...
</a></i> for information on suggesting changes.