| 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> |
| 2 | <html> |
| 3 | <head> |
| 4 | <link rel="STYLESHEET" href="ext.css" type='text/css' /> |
| 5 | <link rel="SHORTCUT ICON" href="../icons/pyfav.png" type="image/png" /> |
| 6 | <link rel='start' href='../index.html' title='Python Documentation Index' /> |
| 7 | <link rel="first" href="ext.html" title='Extending and Embedding the Python Interpreter' /> |
| 8 | <link rel='contents' href='contents.html' title="Contents" /> |
| 9 | <link rel='last' href='about.html' title='About this document...' /> |
| 10 | <link rel='help' href='about.html' title='About this document...' /> |
| 11 | <link rel="next" href="node24.html" /> |
| 12 | <link rel="prev" href="node22.html" /> |
| 13 | <link rel="parent" href="dnt-basics.html" /> |
| 14 | <link rel="next" href="node24.html" /> |
| 15 | <meta name='aesop' content='information' /> |
| 16 | <title>2.1.2 Providing finer control over data attributes</title> |
| 17 | </head> |
| 18 | <body> |
| 19 | <DIV CLASS="navigation"> |
| 20 | <div id='top-navigation-panel' xml:id='top-navigation-panel'> |
| 21 | <table align="center" width="100%" cellpadding="0" cellspacing="2"> |
| 22 | <tr> |
| 23 | <td class='online-navigation'><a rel="prev" title="2.1.1 Adding data and" |
| 24 | href="node22.html"><img src='../icons/previous.png' |
| 25 | border='0' height='32' alt='Previous Page' width='32' /></A></td> |
| 26 | <td class='online-navigation'><a rel="parent" title="2.1 The Basics" |
| 27 | href="dnt-basics.html"><img src='../icons/up.png' |
| 28 | border='0' height='32' alt='Up One Level' width='32' /></A></td> |
| 29 | <td class='online-navigation'><a rel="next" title="2.1.3 Supporting cyclic garbage" |
| 30 | href="node24.html"><img src='../icons/next.png' |
| 31 | border='0' height='32' alt='Next Page' width='32' /></A></td> |
| 32 | <td align="center" width="100%">Extending and Embedding the Python Interpreter</td> |
| 33 | <td class='online-navigation'><a rel="contents" title="Table of Contents" |
| 34 | href="contents.html"><img src='../icons/contents.png' |
| 35 | border='0' height='32' alt='Contents' width='32' /></A></td> |
| 36 | <td class='online-navigation'><img src='../icons/blank.png' |
| 37 | border='0' height='32' alt='' width='32' /></td> |
| 38 | <td class='online-navigation'><img src='../icons/blank.png' |
| 39 | border='0' height='32' alt='' width='32' /></td> |
| 40 | </tr></table> |
| 41 | <div class='online-navigation'> |
| 42 | <b class="navlabel">Previous:</b> |
| 43 | <a class="sectref" rel="prev" href="node22.html">2.1.1 Adding data and</A> |
| 44 | <b class="navlabel">Up:</b> |
| 45 | <a class="sectref" rel="parent" href="dnt-basics.html">2.1 The Basics</A> |
| 46 | <b class="navlabel">Next:</b> |
| 47 | <a class="sectref" rel="next" href="node24.html">2.1.3 Supporting cyclic garbage</A> |
| 48 | </div> |
| 49 | <hr /></div> |
| 50 | </DIV> |
| 51 | <!--End of Navigation Panel--> |
| 52 | |
| 53 | <H2><A NAME="SECTION004120000000000000000"> |
| 54 | 2.1.2 Providing finer control over data attributes</A> |
| 55 | </H2> |
| 56 | |
| 57 | <P> |
| 58 | In this section, we'll provide finer control over how the |
| 59 | <tt class="member">first</tt> and <tt class="member">last</tt> attributes are set in the |
| 60 | <tt class="class">Noddy</tt> example. In the previous version of our module, the |
| 61 | instance variables <tt class="member">first</tt> and <tt class="member">last</tt> could be set to |
| 62 | non-string values or even deleted. We want to make sure that these |
| 63 | attributes always contain strings. |
| 64 | |
| 65 | <P> |
| 66 | <div class="verbatim"> |
| 67 | <pre>#include <Python.h> |
| 68 | #include "structmember.h" |
| 69 | |
| 70 | typedef struct { |
| 71 | PyObject_HEAD |
| 72 | PyObject *first; |
| 73 | PyObject *last; |
| 74 | int number; |
| 75 | } Noddy; |
| 76 | |
| 77 | static void |
| 78 | Noddy_dealloc(Noddy* self) |
| 79 | { |
| 80 | Py_XDECREF(self->first); |
| 81 | Py_XDECREF(self->last); |
| 82 | self->ob_type->tp_free((PyObject*)self); |
| 83 | } |
| 84 | |
| 85 | static PyObject * |
| 86 | Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
| 87 | { |
| 88 | Noddy *self; |
| 89 | |
| 90 | self = (Noddy *)type->tp_alloc(type, 0); |
| 91 | if (self != NULL) { |
| 92 | self->first = PyString_FromString(""); |
| 93 | if (self->first == NULL) |
| 94 | { |
| 95 | Py_DECREF(self); |
| 96 | return NULL; |
| 97 | } |
| 98 | |
| 99 | self->last = PyString_FromString(""); |
| 100 | if (self->last == NULL) |
| 101 | { |
| 102 | Py_DECREF(self); |
| 103 | return NULL; |
| 104 | } |
| 105 | |
| 106 | self->number = 0; |
| 107 | } |
| 108 | |
| 109 | return (PyObject *)self; |
| 110 | } |
| 111 | |
| 112 | static int |
| 113 | Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) |
| 114 | { |
| 115 | PyObject *first=NULL, *last=NULL, *tmp; |
| 116 | |
| 117 | static char *kwlist[] = {"first", "last", "number", NULL}; |
| 118 | |
| 119 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, |
| 120 | &first, &last, |
| 121 | &self->number)) |
| 122 | return -1; |
| 123 | |
| 124 | if (first) { |
| 125 | tmp = self->first; |
| 126 | Py_INCREF(first); |
| 127 | self->first = first; |
| 128 | Py_DECREF(tmp); |
| 129 | } |
| 130 | |
| 131 | if (last) { |
| 132 | tmp = self->last; |
| 133 | Py_INCREF(last); |
| 134 | self->last = last; |
| 135 | Py_DECREF(tmp); |
| 136 | } |
| 137 | |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | static PyMemberDef Noddy_members[] = { |
| 142 | {"number", T_INT, offsetof(Noddy, number), 0, |
| 143 | "noddy number"}, |
| 144 | {NULL} /* Sentinel */ |
| 145 | }; |
| 146 | |
| 147 | static PyObject * |
| 148 | Noddy_getfirst(Noddy *self, void *closure) |
| 149 | { |
| 150 | Py_INCREF(self->first); |
| 151 | return self->first; |
| 152 | } |
| 153 | |
| 154 | static int |
| 155 | Noddy_setfirst(Noddy *self, PyObject *value, void *closure) |
| 156 | { |
| 157 | if (value == NULL) { |
| 158 | PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); |
| 159 | return -1; |
| 160 | } |
| 161 | |
| 162 | if (! PyString_Check(value)) { |
| 163 | PyErr_SetString(PyExc_TypeError, |
| 164 | "The first attribute value must be a string"); |
| 165 | return -1; |
| 166 | } |
| 167 | |
| 168 | Py_DECREF(self->first); |
| 169 | Py_INCREF(value); |
| 170 | self->first = value; |
| 171 | |
| 172 | return 0; |
| 173 | } |
| 174 | |
| 175 | static PyObject * |
| 176 | Noddy_getlast(Noddy *self, void *closure) |
| 177 | { |
| 178 | Py_INCREF(self->last); |
| 179 | return self->last; |
| 180 | } |
| 181 | |
| 182 | static int |
| 183 | Noddy_setlast(Noddy *self, PyObject *value, void *closure) |
| 184 | { |
| 185 | if (value == NULL) { |
| 186 | PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute"); |
| 187 | return -1; |
| 188 | } |
| 189 | |
| 190 | if (! PyString_Check(value)) { |
| 191 | PyErr_SetString(PyExc_TypeError, |
| 192 | "The last attribute value must be a string"); |
| 193 | return -1; |
| 194 | } |
| 195 | |
| 196 | Py_DECREF(self->last); |
| 197 | Py_INCREF(value); |
| 198 | self->last = value; |
| 199 | |
| 200 | return 0; |
| 201 | } |
| 202 | |
| 203 | static PyGetSetDef Noddy_getseters[] = { |
| 204 | {"first", |
| 205 | (getter)Noddy_getfirst, (setter)Noddy_setfirst, |
| 206 | "first name", |
| 207 | NULL}, |
| 208 | {"last", |
| 209 | (getter)Noddy_getlast, (setter)Noddy_setlast, |
| 210 | "last name", |
| 211 | NULL}, |
| 212 | {NULL} /* Sentinel */ |
| 213 | }; |
| 214 | |
| 215 | static PyObject * |
| 216 | Noddy_name(Noddy* self) |
| 217 | { |
| 218 | static PyObject *format = NULL; |
| 219 | PyObject *args, *result; |
| 220 | |
| 221 | if (format == NULL) { |
| 222 | format = PyString_FromString("%s %s"); |
| 223 | if (format == NULL) |
| 224 | return NULL; |
| 225 | } |
| 226 | |
| 227 | args = Py_BuildValue("OO", self->first, self->last); |
| 228 | if (args == NULL) |
| 229 | return NULL; |
| 230 | |
| 231 | result = PyString_Format(format, args); |
| 232 | Py_DECREF(args); |
| 233 | |
| 234 | return result; |
| 235 | } |
| 236 | |
| 237 | static PyMethodDef Noddy_methods[] = { |
| 238 | {"name", (PyCFunction)Noddy_name, METH_NOARGS, |
| 239 | "Return the name, combining the first and last name" |
| 240 | }, |
| 241 | {NULL} /* Sentinel */ |
| 242 | }; |
| 243 | |
| 244 | static PyTypeObject NoddyType = { |
| 245 | PyObject_HEAD_INIT(NULL) |
| 246 | 0, /*ob_size*/ |
| 247 | "noddy.Noddy", /*tp_name*/ |
| 248 | sizeof(Noddy), /*tp_basicsize*/ |
| 249 | 0, /*tp_itemsize*/ |
| 250 | (destructor)Noddy_dealloc, /*tp_dealloc*/ |
| 251 | 0, /*tp_print*/ |
| 252 | 0, /*tp_getattr*/ |
| 253 | 0, /*tp_setattr*/ |
| 254 | 0, /*tp_compare*/ |
| 255 | 0, /*tp_repr*/ |
| 256 | 0, /*tp_as_number*/ |
| 257 | 0, /*tp_as_sequence*/ |
| 258 | 0, /*tp_as_mapping*/ |
| 259 | 0, /*tp_hash */ |
| 260 | 0, /*tp_call*/ |
| 261 | 0, /*tp_str*/ |
| 262 | 0, /*tp_getattro*/ |
| 263 | 0, /*tp_setattro*/ |
| 264 | 0, /*tp_as_buffer*/ |
| 265 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ |
| 266 | "Noddy objects", /* tp_doc */ |
| 267 | 0, /* tp_traverse */ |
| 268 | 0, /* tp_clear */ |
| 269 | 0, /* tp_richcompare */ |
| 270 | 0, /* tp_weaklistoffset */ |
| 271 | 0, /* tp_iter */ |
| 272 | 0, /* tp_iternext */ |
| 273 | Noddy_methods, /* tp_methods */ |
| 274 | Noddy_members, /* tp_members */ |
| 275 | Noddy_getseters, /* tp_getset */ |
| 276 | 0, /* tp_base */ |
| 277 | 0, /* tp_dict */ |
| 278 | 0, /* tp_descr_get */ |
| 279 | 0, /* tp_descr_set */ |
| 280 | 0, /* tp_dictoffset */ |
| 281 | (initproc)Noddy_init, /* tp_init */ |
| 282 | 0, /* tp_alloc */ |
| 283 | Noddy_new, /* tp_new */ |
| 284 | }; |
| 285 | |
| 286 | static PyMethodDef module_methods[] = { |
| 287 | {NULL} /* Sentinel */ |
| 288 | }; |
| 289 | |
| 290 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ |
| 291 | #define PyMODINIT_FUNC void |
| 292 | #endif |
| 293 | PyMODINIT_FUNC |
| 294 | initnoddy3(void) |
| 295 | { |
| 296 | PyObject* m; |
| 297 | |
| 298 | if (PyType_Ready(&NoddyType) < 0) |
| 299 | return; |
| 300 | |
| 301 | m = Py_InitModule3("noddy3", module_methods, |
| 302 | "Example module that creates an extension type."); |
| 303 | |
| 304 | if (m == NULL) |
| 305 | return; |
| 306 | |
| 307 | Py_INCREF(&NoddyType); |
| 308 | PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType); |
| 309 | } |
| 310 | </pre> |
| 311 | <div class="footer"> |
| 312 | <a href="noddy3.txt" type="text/plain">Download as text (original file name: <span class="file">noddy3.c</span>).</a> |
| 313 | </div></div> |
| 314 | |
| 315 | <P> |
| 316 | To provide greater control, over the <tt class="member">first</tt> and <tt class="member">last</tt> |
| 317 | attributes, we'll use custom getter and setter functions. Here are |
| 318 | the functions for getting and setting the <tt class="member">first</tt> attribute: |
| 319 | |
| 320 | <P> |
| 321 | <div class="verbatim"><pre> |
| 322 | Noddy_getfirst(Noddy *self, void *closure) |
| 323 | { |
| 324 | Py_INCREF(self->first); |
| 325 | return self->first; |
| 326 | } |
| 327 | |
| 328 | static int |
| 329 | Noddy_setfirst(Noddy *self, PyObject *value, void *closure) |
| 330 | { |
| 331 | if (value == NULL) { |
| 332 | PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); |
| 333 | return -1; |
| 334 | } |
| 335 | |
| 336 | if (! PyString_Check(value)) { |
| 337 | PyErr_SetString(PyExc_TypeError, |
| 338 | "The first attribute value must be a string"); |
| 339 | return -1; |
| 340 | } |
| 341 | |
| 342 | Py_DECREF(self->first); |
| 343 | Py_INCREF(value); |
| 344 | self->first = value; |
| 345 | |
| 346 | return 0; |
| 347 | } |
| 348 | </pre></div> |
| 349 | |
| 350 | <P> |
| 351 | The getter function is passed a <tt class="class">Noddy</tt> object and a |
| 352 | ``closure'', which is void pointer. In this case, the closure is |
| 353 | ignored. (The closure supports an advanced usage in which definition |
| 354 | data is passed to the getter and setter. This could, for example, be |
| 355 | used to allow a single set of getter and setter functions that decide |
| 356 | the attribute to get or set based on data in the closure.) |
| 357 | |
| 358 | <P> |
| 359 | The setter function is passed the <tt class="class">Noddy</tt> object, the new value, |
| 360 | and the closure. The new value may be <tt class="constant">NULL</tt>, in which case the |
| 361 | attribute is being deleted. In our setter, we raise an error if the |
| 362 | attribute is deleted or if the attribute value is not a string. |
| 363 | |
| 364 | <P> |
| 365 | We create an array of <tt class="ctype">PyGetSetDef</tt> structures: |
| 366 | |
| 367 | <P> |
| 368 | <div class="verbatim"><pre> |
| 369 | static PyGetSetDef Noddy_getseters[] = { |
| 370 | {"first", |
| 371 | (getter)Noddy_getfirst, (setter)Noddy_setfirst, |
| 372 | "first name", |
| 373 | NULL}, |
| 374 | {"last", |
| 375 | (getter)Noddy_getlast, (setter)Noddy_setlast, |
| 376 | "last name", |
| 377 | NULL}, |
| 378 | {NULL} /* Sentinel */ |
| 379 | }; |
| 380 | </pre></div> |
| 381 | |
| 382 | <P> |
| 383 | and register it in the <tt class="member">tp_getset</tt> slot: |
| 384 | |
| 385 | <P> |
| 386 | <div class="verbatim"><pre> |
| 387 | Noddy_getseters, /* tp_getset */ |
| 388 | </pre></div> |
| 389 | |
| 390 | <P> |
| 391 | to register out attribute getters and setters. |
| 392 | |
| 393 | <P> |
| 394 | The last item in a <tt class="ctype">PyGetSetDef</tt> structure is the closure |
| 395 | mentioned above. In this case, we aren't using the closure, so we just |
| 396 | pass <tt class="constant">NULL</tt>. |
| 397 | |
| 398 | <P> |
| 399 | We also remove the member definitions for these attributes: |
| 400 | |
| 401 | <P> |
| 402 | <div class="verbatim"><pre> |
| 403 | static PyMemberDef Noddy_members[] = { |
| 404 | {"number", T_INT, offsetof(Noddy, number), 0, |
| 405 | "noddy number"}, |
| 406 | {NULL} /* Sentinel */ |
| 407 | }; |
| 408 | </pre></div> |
| 409 | |
| 410 | <P> |
| 411 | We also need to update the <tt class="member">tp_init</tt> handler to only allow |
| 412 | strings<A NAME="tex2html7" |
| 413 | HREF="#foot1157"><SUP>2.3</SUP></A> to be passed: |
| 414 | |
| 415 | <P> |
| 416 | <div class="verbatim"><pre> |
| 417 | static int |
| 418 | Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) |
| 419 | { |
| 420 | PyObject *first=NULL, *last=NULL, *tmp; |
| 421 | |
| 422 | static char *kwlist[] = {"first", "last", "number", NULL}; |
| 423 | |
| 424 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, |
| 425 | &first, &last, |
| 426 | &self->number)) |
| 427 | return -1; |
| 428 | |
| 429 | if (first) { |
| 430 | tmp = self->first; |
| 431 | Py_INCREF(first); |
| 432 | self->first = first; |
| 433 | Py_DECREF(tmp); |
| 434 | } |
| 435 | |
| 436 | if (last) { |
| 437 | tmp = self->last; |
| 438 | Py_INCREF(last); |
| 439 | self->last = last; |
| 440 | Py_DECREF(tmp); |
| 441 | } |
| 442 | |
| 443 | return 0; |
| 444 | } |
| 445 | </pre></div> |
| 446 | |
| 447 | <P> |
| 448 | With these changes, we can assure that the <tt class="member">first</tt> and |
| 449 | <tt class="member">last</tt> members are never <tt class="constant">NULL</tt> so we can remove checks for <tt class="constant">NULL</tt> |
| 450 | values in almost all cases. This means that most of the |
| 451 | <tt class="cfunction">Py_XDECREF()</tt> calls can be converted to <tt class="cfunction">Py_DECREF()</tt> |
| 452 | calls. The only place we can't change these calls is in the |
| 453 | deallocator, where there is the possibility that the initialization of |
| 454 | these members failed in the constructor. |
| 455 | |
| 456 | <P> |
| 457 | We also rename the module initialization function and module name in |
| 458 | the initialization function, as we did before, and we add an extra |
| 459 | definition to the <span class="file">setup.py</span> file. |
| 460 | |
| 461 | <P> |
| 462 | <BR><HR><H4>Footnotes</H4> |
| 463 | <DL> |
| 464 | <DT><A NAME="foot1157">... |
| 465 | strings</A><A |
| 466 | HREF="node23.html#tex2html7"><SUP>2.3</SUP></A></DT> |
| 467 | <DD>We now know that the first and last members are strings, |
| 468 | so perhaps we could be less careful about decrementing their |
| 469 | reference counts, however, we accept instances of string subclasses. |
| 470 | Even though deallocating normal strings won't call back into our |
| 471 | objects, we can't guarantee that deallocating an instance of a string |
| 472 | subclass won't. call back into out objects. |
| 473 | |
| 474 | </DD> |
| 475 | </DL> |
| 476 | <DIV CLASS="navigation"> |
| 477 | <div class='online-navigation'> |
| 478 | <p></p><hr /> |
| 479 | <table align="center" width="100%" cellpadding="0" cellspacing="2"> |
| 480 | <tr> |
| 481 | <td class='online-navigation'><a rel="prev" title="2.1.1 Adding data and" |
| 482 | href="node22.html"><img src='../icons/previous.png' |
| 483 | border='0' height='32' alt='Previous Page' width='32' /></A></td> |
| 484 | <td class='online-navigation'><a rel="parent" title="2.1 The Basics" |
| 485 | href="dnt-basics.html"><img src='../icons/up.png' |
| 486 | border='0' height='32' alt='Up One Level' width='32' /></A></td> |
| 487 | <td class='online-navigation'><a rel="next" title="2.1.3 Supporting cyclic garbage" |
| 488 | href="node24.html"><img src='../icons/next.png' |
| 489 | border='0' height='32' alt='Next Page' width='32' /></A></td> |
| 490 | <td align="center" width="100%">Extending and Embedding the Python Interpreter</td> |
| 491 | <td class='online-navigation'><a rel="contents" title="Table of Contents" |
| 492 | href="contents.html"><img src='../icons/contents.png' |
| 493 | border='0' height='32' alt='Contents' width='32' /></A></td> |
| 494 | <td class='online-navigation'><img src='../icons/blank.png' |
| 495 | border='0' height='32' alt='' width='32' /></td> |
| 496 | <td class='online-navigation'><img src='../icons/blank.png' |
| 497 | border='0' height='32' alt='' width='32' /></td> |
| 498 | </tr></table> |
| 499 | <div class='online-navigation'> |
| 500 | <b class="navlabel">Previous:</b> |
| 501 | <a class="sectref" rel="prev" href="node22.html">2.1.1 Adding data and</A> |
| 502 | <b class="navlabel">Up:</b> |
| 503 | <a class="sectref" rel="parent" href="dnt-basics.html">2.1 The Basics</A> |
| 504 | <b class="navlabel">Next:</b> |
| 505 | <a class="sectref" rel="next" href="node24.html">2.1.3 Supporting cyclic garbage</A> |
| 506 | </div> |
| 507 | </div> |
| 508 | <hr /> |
| 509 | <span class="release-info">Release 2.4.2, documentation updated on 28 September 2005.</span> |
| 510 | </DIV> |
| 511 | <!--End of Navigation Panel--> |
| 512 | <ADDRESS> |
| 513 | See <i><a href="about.html">About this document...</a></i> for information on suggesting changes. |
| 514 | </ADDRESS> |
| 515 | </BODY> |
| 516 | </HTML> |