Commit | Line | Data |
---|---|---|
920dae64 AT |
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="node23.html" /> | |
12 | <link rel="prev" href="dnt-basics.html" /> | |
13 | <link rel="parent" href="dnt-basics.html" /> | |
14 | <link rel="next" href="node23.html" /> | |
15 | <meta name='aesop' content='information' /> | |
16 | <title>2.1.1 Adding data and methods to the Basic example</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 The Basics" | |
24 | href="dnt-basics.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.2 Providing finer control" | |
30 | href="node23.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="dnt-basics.html">2.1 The Basics</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="node23.html">2.1.2 Providing finer control</A> | |
48 | </div> | |
49 | <hr /></div> | |
50 | </DIV> | |
51 | <!--End of Navigation Panel--> | |
52 | ||
53 | <H2><A NAME="SECTION004110000000000000000"> | |
54 | 2.1.1 Adding data and methods to the Basic example</A> | |
55 | </H2> | |
56 | ||
57 | <P> | |
58 | Let's expend the basic example to add some data and methods. Let's | |
59 | also make the type usable as a base class. We'll create | |
60 | a new module, <tt class="module">noddy2</tt> that adds these capabilities: | |
61 | ||
62 | <P> | |
63 | <div class="verbatim"> | |
64 | <pre>#include <Python.h> | |
65 | #include "structmember.h" | |
66 | ||
67 | typedef struct { | |
68 | PyObject_HEAD | |
69 | PyObject *first; /* first name */ | |
70 | PyObject *last; /* last name */ | |
71 | int number; | |
72 | } Noddy; | |
73 | ||
74 | static void | |
75 | Noddy_dealloc(Noddy* self) | |
76 | { | |
77 | Py_XDECREF(self->first); | |
78 | Py_XDECREF(self->last); | |
79 | self->ob_type->tp_free((PyObject*)self); | |
80 | } | |
81 | ||
82 | static PyObject * | |
83 | Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
84 | { | |
85 | Noddy *self; | |
86 | ||
87 | self = (Noddy *)type->tp_alloc(type, 0); | |
88 | if (self != NULL) { | |
89 | self->first = PyString_FromString(""); | |
90 | if (self->first == NULL) | |
91 | { | |
92 | Py_DECREF(self); | |
93 | return NULL; | |
94 | } | |
95 | ||
96 | self->last = PyString_FromString(""); | |
97 | if (self->last == NULL) | |
98 | { | |
99 | Py_DECREF(self); | |
100 | return NULL; | |
101 | } | |
102 | ||
103 | self->number = 0; | |
104 | } | |
105 | ||
106 | return (PyObject *)self; | |
107 | } | |
108 | ||
109 | static int | |
110 | Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) | |
111 | { | |
112 | PyObject *first=NULL, *last=NULL, *tmp; | |
113 | ||
114 | static char *kwlist[] = {"first", "last", "number", NULL}; | |
115 | ||
116 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, | |
117 | &first, &last, | |
118 | &self->number)) | |
119 | return -1; | |
120 | ||
121 | if (first) { | |
122 | tmp = self->first; | |
123 | Py_INCREF(first); | |
124 | self->first = first; | |
125 | Py_XDECREF(tmp); | |
126 | } | |
127 | ||
128 | if (last) { | |
129 | tmp = self->last; | |
130 | Py_INCREF(last); | |
131 | self->last = last; | |
132 | Py_XDECREF(tmp); | |
133 | } | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | static PyMemberDef Noddy_members[] = { | |
139 | {"first", T_OBJECT_EX, offsetof(Noddy, first), 0, | |
140 | "first name"}, | |
141 | {"last", T_OBJECT_EX, offsetof(Noddy, last), 0, | |
142 | "last name"}, | |
143 | {"number", T_INT, offsetof(Noddy, number), 0, | |
144 | "noddy number"}, | |
145 | {NULL} /* Sentinel */ | |
146 | }; | |
147 | ||
148 | static PyObject * | |
149 | Noddy_name(Noddy* self) | |
150 | { | |
151 | static PyObject *format = NULL; | |
152 | PyObject *args, *result; | |
153 | ||
154 | if (format == NULL) { | |
155 | format = PyString_FromString("%s %s"); | |
156 | if (format == NULL) | |
157 | return NULL; | |
158 | } | |
159 | ||
160 | if (self->first == NULL) { | |
161 | PyErr_SetString(PyExc_AttributeError, "first"); | |
162 | return NULL; | |
163 | } | |
164 | ||
165 | if (self->last == NULL) { | |
166 | PyErr_SetString(PyExc_AttributeError, "last"); | |
167 | return NULL; | |
168 | } | |
169 | ||
170 | args = Py_BuildValue("OO", self->first, self->last); | |
171 | if (args == NULL) | |
172 | return NULL; | |
173 | ||
174 | result = PyString_Format(format, args); | |
175 | Py_DECREF(args); | |
176 | ||
177 | return result; | |
178 | } | |
179 | ||
180 | static PyMethodDef Noddy_methods[] = { | |
181 | {"name", (PyCFunction)Noddy_name, METH_NOARGS, | |
182 | "Return the name, combining the first and last name" | |
183 | }, | |
184 | {NULL} /* Sentinel */ | |
185 | }; | |
186 | ||
187 | static PyTypeObject NoddyType = { | |
188 | PyObject_HEAD_INIT(NULL) | |
189 | 0, /*ob_size*/ | |
190 | "noddy.Noddy", /*tp_name*/ | |
191 | sizeof(Noddy), /*tp_basicsize*/ | |
192 | 0, /*tp_itemsize*/ | |
193 | (destructor)Noddy_dealloc, /*tp_dealloc*/ | |
194 | 0, /*tp_print*/ | |
195 | 0, /*tp_getattr*/ | |
196 | 0, /*tp_setattr*/ | |
197 | 0, /*tp_compare*/ | |
198 | 0, /*tp_repr*/ | |
199 | 0, /*tp_as_number*/ | |
200 | 0, /*tp_as_sequence*/ | |
201 | 0, /*tp_as_mapping*/ | |
202 | 0, /*tp_hash */ | |
203 | 0, /*tp_call*/ | |
204 | 0, /*tp_str*/ | |
205 | 0, /*tp_getattro*/ | |
206 | 0, /*tp_setattro*/ | |
207 | 0, /*tp_as_buffer*/ | |
208 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ | |
209 | "Noddy objects", /* tp_doc */ | |
210 | 0, /* tp_traverse */ | |
211 | 0, /* tp_clear */ | |
212 | 0, /* tp_richcompare */ | |
213 | 0, /* tp_weaklistoffset */ | |
214 | 0, /* tp_iter */ | |
215 | 0, /* tp_iternext */ | |
216 | Noddy_methods, /* tp_methods */ | |
217 | Noddy_members, /* tp_members */ | |
218 | 0, /* tp_getset */ | |
219 | 0, /* tp_base */ | |
220 | 0, /* tp_dict */ | |
221 | 0, /* tp_descr_get */ | |
222 | 0, /* tp_descr_set */ | |
223 | 0, /* tp_dictoffset */ | |
224 | (initproc)Noddy_init, /* tp_init */ | |
225 | 0, /* tp_alloc */ | |
226 | Noddy_new, /* tp_new */ | |
227 | }; | |
228 | ||
229 | static PyMethodDef module_methods[] = { | |
230 | {NULL} /* Sentinel */ | |
231 | }; | |
232 | ||
233 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ | |
234 | #define PyMODINIT_FUNC void | |
235 | #endif | |
236 | PyMODINIT_FUNC | |
237 | initnoddy2(void) | |
238 | { | |
239 | PyObject* m; | |
240 | ||
241 | if (PyType_Ready(&NoddyType) < 0) | |
242 | return; | |
243 | ||
244 | m = Py_InitModule3("noddy2", module_methods, | |
245 | "Example module that creates an extension type."); | |
246 | ||
247 | if (m == NULL) | |
248 | return; | |
249 | ||
250 | Py_INCREF(&NoddyType); | |
251 | PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType); | |
252 | } | |
253 | </pre> | |
254 | <div class="footer"> | |
255 | <a href="noddy2.txt" type="text/plain">Download as text (original file name: <span class="file">noddy2.c</span>).</a> | |
256 | </div></div> | |
257 | ||
258 | <P> | |
259 | This version of the module has a number of changes. | |
260 | ||
261 | <P> | |
262 | We've added an extra include: | |
263 | ||
264 | <P> | |
265 | <div class="verbatim"><pre> | |
266 | #include "structmember.h" | |
267 | </pre></div> | |
268 | ||
269 | <P> | |
270 | This include provides declarations that we use to handle attributes, | |
271 | as described a bit later. | |
272 | ||
273 | <P> | |
274 | The name of the <tt class="class">Noddy</tt> object structure has been shortened to | |
275 | <tt class="class">Noddy</tt>. The type object name has been shortened to | |
276 | <tt class="class">NoddyType</tt>. | |
277 | ||
278 | <P> | |
279 | The <tt class="class">Noddy</tt> type now has three data attributes, <var>first</var>, | |
280 | <var>last</var>, and <var>number</var>. The <var>first</var> and <var>last</var> | |
281 | variables are Python strings containing first and last names. The | |
282 | <var>number</var> attribute is an integer. | |
283 | ||
284 | <P> | |
285 | The object structure is updated accordingly: | |
286 | ||
287 | <P> | |
288 | <div class="verbatim"><pre> | |
289 | typedef struct { | |
290 | PyObject_HEAD | |
291 | PyObject *first; | |
292 | PyObject *last; | |
293 | int number; | |
294 | } Noddy; | |
295 | </pre></div> | |
296 | ||
297 | <P> | |
298 | Because we now have data to manage, we have to be more careful about | |
299 | object allocation and deallocation. At a minimum, we need a | |
300 | deallocation method: | |
301 | ||
302 | <P> | |
303 | <div class="verbatim"><pre> | |
304 | static void | |
305 | Noddy_dealloc(Noddy* self) | |
306 | { | |
307 | Py_XDECREF(self->first); | |
308 | Py_XDECREF(self->last); | |
309 | self->ob_type->tp_free((PyObject*)self); | |
310 | } | |
311 | </pre></div> | |
312 | ||
313 | <P> | |
314 | which is assigned to the <tt class="member">tp_dealloc</tt> member: | |
315 | ||
316 | <P> | |
317 | <div class="verbatim"><pre> | |
318 | (destructor)Noddy_dealloc, /*tp_dealloc*/ | |
319 | </pre></div> | |
320 | ||
321 | <P> | |
322 | This method decrements the reference counts of the two Python | |
323 | attributes. We use <tt class="cfunction">Py_XDECREF()</tt> here because the | |
324 | <tt class="member">first</tt> and <tt class="member">last</tt> members could be <tt class="constant">NULL</tt>. It then | |
325 | calls the <tt class="member">tp_free</tt> member of the object's type to free the | |
326 | object's memory. Note that the object's type might not be | |
327 | <tt class="class">NoddyType</tt>, because the object may be an instance of a | |
328 | subclass. | |
329 | ||
330 | <P> | |
331 | We want to make sure that the first and last names are initialized to | |
332 | empty strings, so we provide a new method: | |
333 | ||
334 | <P> | |
335 | <div class="verbatim"><pre> | |
336 | static PyObject * | |
337 | Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
338 | { | |
339 | Noddy *self; | |
340 | ||
341 | self = (Noddy *)type->tp_alloc(type, 0); | |
342 | if (self != NULL) { | |
343 | self->first = PyString_FromString(""); | |
344 | if (self->first == NULL) | |
345 | { | |
346 | Py_DECREF(self); | |
347 | return NULL; | |
348 | } | |
349 | ||
350 | self->last = PyString_FromString(""); | |
351 | if (self->last == NULL) | |
352 | { | |
353 | Py_DECREF(self); | |
354 | return NULL; | |
355 | } | |
356 | ||
357 | self->number = 0; | |
358 | } | |
359 | ||
360 | return (PyObject *)self; | |
361 | } | |
362 | </pre></div> | |
363 | ||
364 | <P> | |
365 | and install it in the <tt class="member">tp_new</tt> member: | |
366 | ||
367 | <P> | |
368 | <div class="verbatim"><pre> | |
369 | Noddy_new, /* tp_new */ | |
370 | </pre></div> | |
371 | ||
372 | <P> | |
373 | The new member is responsible for creating (as opposed to | |
374 | initializing) objects of the type. It is exposed in Python as the | |
375 | <tt class="method">__new__()</tt> method. See the paper titled ``Unifying types and | |
376 | classes in Python'' for a detailed discussion of the <tt class="method">__new__()</tt> | |
377 | method. One reason to implement a new method is to assure the initial | |
378 | values of instance variables. In this case, we use the new method to | |
379 | make sure that the initial values of the members <tt class="member">first</tt> and | |
380 | <tt class="member">last</tt> are not <tt class="constant">NULL</tt>. If we didn't care whether the initial | |
381 | values were <tt class="constant">NULL</tt>, we could have used <tt class="cfunction">PyType_GenericNew()</tt> as | |
382 | our new method, as we did before. <tt class="cfunction">PyType_GenericNew()</tt> | |
383 | initializes all of the instance variable members to <tt class="constant">NULL</tt>. | |
384 | ||
385 | <P> | |
386 | The new method is a static method that is passed the type being | |
387 | instantiated and any arguments passed when the type was called, | |
388 | and that returns the new object created. New methods always accept | |
389 | positional and keyword arguments, but they often ignore the arguments, | |
390 | leaving the argument handling to initializer methods. Note that if the | |
391 | type supports subclassing, the type passed may not be the type being | |
392 | defined. The new method calls the tp_alloc slot to allocate memory. | |
393 | We don't fill the <tt class="member">tp_alloc</tt> slot ourselves. Rather | |
394 | <tt class="cfunction">PyType_Ready()</tt> fills it for us by inheriting it from our | |
395 | base class, which is <tt class="class">object</tt> by default. Most types use the | |
396 | default allocation. | |
397 | ||
398 | <P> | |
399 | <span class="note"><b class="label">Note:</b> | |
400 | If you are creating a co-operative <tt class="member">tp_new</tt> (one that | |
401 | calls a base type's <tt class="member">tp_new</tt> or <tt class="method">__new__</tt>), you | |
402 | must <em>not</em> try to determine what method to call using | |
403 | method resolution order at runtime. Always statically determine | |
404 | what type you are going to call, and call its <tt class="member">tp_new</tt> | |
405 | directly, or via <code>type->tp_base->tp_new</code>. If you do | |
406 | not do this, Python subclasses of your type that also inherit | |
407 | from other Python-defined classes may not work correctly. | |
408 | (Specifically, you may not be able to create instances of | |
409 | such subclasses without getting a <tt class="exception">TypeError</tt>.)</span> | |
410 | ||
411 | <P> | |
412 | We provide an initialization function: | |
413 | ||
414 | <P> | |
415 | <div class="verbatim"><pre> | |
416 | static int | |
417 | Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) | |
418 | { | |
419 | PyObject *first=NULL, *last=NULL, *tmp; | |
420 | ||
421 | static char *kwlist[] = {"first", "last", "number", NULL}; | |
422 | ||
423 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, | |
424 | &first, &last, | |
425 | &self->number)) | |
426 | return -1; | |
427 | ||
428 | if (first) { | |
429 | tmp = self->first; | |
430 | Py_INCREF(first); | |
431 | self->first = first; | |
432 | Py_XDECREF(tmp); | |
433 | } | |
434 | ||
435 | if (last) { | |
436 | tmp = self->last; | |
437 | Py_INCREF(last); | |
438 | self->last = last; | |
439 | Py_XDECREF(tmp); | |
440 | } | |
441 | ||
442 | return 0; | |
443 | } | |
444 | </pre></div> | |
445 | ||
446 | <P> | |
447 | by filling the <tt class="member">tp_init</tt> slot. | |
448 | ||
449 | <P> | |
450 | <div class="verbatim"><pre> | |
451 | (initproc)Noddy_init, /* tp_init */ | |
452 | </pre></div> | |
453 | ||
454 | <P> | |
455 | The <tt class="member">tp_init</tt> slot is exposed in Python as the | |
456 | <tt class="method">__init__()</tt> method. It is used to initialize an object after | |
457 | it's created. Unlike the new method, we can't guarantee that the | |
458 | initializer is called. The initializer isn't called when unpickling | |
459 | objects and it can be overridden. Our initializer accepts arguments | |
460 | to provide initial values for our instance. Initializers always accept | |
461 | positional and keyword arguments. | |
462 | ||
463 | <P> | |
464 | Initializers can be called multiple times. Anyone can call the | |
465 | <tt class="method">__init__()</tt> method on our objects. For this reason, we have | |
466 | to be extra careful when assigning the new values. We might be | |
467 | tempted, for example to assign the <tt class="member">first</tt> member like this: | |
468 | ||
469 | <P> | |
470 | <div class="verbatim"><pre> | |
471 | if (first) { | |
472 | Py_XDECREF(self->first); | |
473 | Py_INCREF(first); | |
474 | self->first = first; | |
475 | } | |
476 | </pre></div> | |
477 | ||
478 | <P> | |
479 | But this would be risky. Our type doesn't restrict the type of the | |
480 | <tt class="member">first</tt> member, so it could be any kind of object. It could | |
481 | have a destructor that causes code to be executed that tries to | |
482 | access the <tt class="member">first</tt> member. To be paranoid and protect | |
483 | ourselves against this possibility, we almost always reassign members | |
484 | before decrementing their reference counts. When don't we have to do | |
485 | this? | |
486 | ||
487 | <UL> | |
488 | <LI>when we absolutely know that the reference count is greater than | |
489 | 1 | |
490 | </LI> | |
491 | <LI>when we know that deallocation of the object<A NAME="tex2html5" | |
492 | HREF="#foot1098"><SUP>2.1</SUP></A> will not cause any | |
493 | calls back into our type's code | |
494 | </LI> | |
495 | <LI>when decrementing a reference count in a <tt class="member">tp_dealloc</tt> | |
496 | handler when garbage-collections is not supported<A NAME="tex2html6" | |
497 | HREF="#foot1418"><SUP>2.2</SUP></A> | |
498 | </LI> | |
499 | <LI> | |
500 | </LI> | |
501 | </UL> | |
502 | ||
503 | <P> | |
504 | We want to want to expose our instance variables as attributes. There | |
505 | are a number of ways to do that. The simplest way is to define member | |
506 | definitions: | |
507 | ||
508 | <P> | |
509 | <div class="verbatim"><pre> | |
510 | static PyMemberDef Noddy_members[] = { | |
511 | {"first", T_OBJECT_EX, offsetof(Noddy, first), 0, | |
512 | "first name"}, | |
513 | {"last", T_OBJECT_EX, offsetof(Noddy, last), 0, | |
514 | "last name"}, | |
515 | {"number", T_INT, offsetof(Noddy, number), 0, | |
516 | "noddy number"}, | |
517 | {NULL} /* Sentinel */ | |
518 | }; | |
519 | </pre></div> | |
520 | ||
521 | <P> | |
522 | and put the definitions in the <tt class="member">tp_members</tt> slot: | |
523 | ||
524 | <P> | |
525 | <div class="verbatim"><pre> | |
526 | Noddy_members, /* tp_members */ | |
527 | </pre></div> | |
528 | ||
529 | <P> | |
530 | Each member definition has a member name, type, offset, access flags | |
531 | and documentation string. See the ``Generic Attribute Management'' | |
532 | section below for details. | |
533 | ||
534 | <P> | |
535 | A disadvantage of this approach is that it doesn't provide a way to | |
536 | restrict the types of objects that can be assigned to the Python | |
537 | attributes. We expect the first and last names to be strings, but any | |
538 | Python objects can be assigned. Further, the attributes can be | |
539 | deleted, setting the C pointers to <tt class="constant">NULL</tt>. Even though we can make | |
540 | sure the members are initialized to non-<tt class="constant">NULL</tt> values, the members can | |
541 | be set to <tt class="constant">NULL</tt> if the attributes are deleted. | |
542 | ||
543 | <P> | |
544 | We define a single method, <tt class="method">name</tt>, that outputs the objects | |
545 | name as the concatenation of the first and last names. | |
546 | ||
547 | <P> | |
548 | <div class="verbatim"><pre> | |
549 | static PyObject * | |
550 | Noddy_name(Noddy* self) | |
551 | { | |
552 | static PyObject *format = NULL; | |
553 | PyObject *args, *result; | |
554 | ||
555 | if (format == NULL) { | |
556 | format = PyString_FromString("%s %s"); | |
557 | if (format == NULL) | |
558 | return NULL; | |
559 | } | |
560 | ||
561 | if (self->first == NULL) { | |
562 | PyErr_SetString(PyExc_AttributeError, "first"); | |
563 | return NULL; | |
564 | } | |
565 | ||
566 | if (self->last == NULL) { | |
567 | PyErr_SetString(PyExc_AttributeError, "last"); | |
568 | return NULL; | |
569 | } | |
570 | ||
571 | args = Py_BuildValue("OO", self->first, self->last); | |
572 | if (args == NULL) | |
573 | return NULL; | |
574 | ||
575 | result = PyString_Format(format, args); | |
576 | Py_DECREF(args); | |
577 | ||
578 | return result; | |
579 | } | |
580 | </pre></div> | |
581 | ||
582 | <P> | |
583 | The method is implemented as a C function that takes a <tt class="class">Noddy</tt> (or | |
584 | <tt class="class">Noddy</tt> subclass) instance as the first argument. Methods | |
585 | always take an instance as the first argument. Methods often take | |
586 | positional and keyword arguments as well, but in this cased we don't | |
587 | take any and don't need to accept a positional argument tuple or | |
588 | keyword argument dictionary. This method is equivalent to the Python | |
589 | method: | |
590 | ||
591 | <P> | |
592 | <div class="verbatim"><pre> | |
593 | def name(self): | |
594 | return "%s %s" % (self.first, self.last) | |
595 | </pre></div> | |
596 | ||
597 | <P> | |
598 | Note that we have to check for the possibility that our <tt class="member">first</tt> | |
599 | and <tt class="member">last</tt> members are <tt class="constant">NULL</tt>. This is because they can be | |
600 | deleted, in which case they are set to <tt class="constant">NULL</tt>. It would be better to | |
601 | prevent deletion of these attributes and to restrict the attribute | |
602 | values to be strings. We'll see how to do that in the next section. | |
603 | ||
604 | <P> | |
605 | Now that we've defined the method, we need to create an array of | |
606 | method definitions: | |
607 | ||
608 | <P> | |
609 | <div class="verbatim"><pre> | |
610 | static PyMethodDef Noddy_methods[] = { | |
611 | {"name", (PyCFunction)Noddy_name, METH_NOARGS, | |
612 | "Return the name, combining the first and last name" | |
613 | }, | |
614 | {NULL} /* Sentinel */ | |
615 | }; | |
616 | </pre></div> | |
617 | ||
618 | <P> | |
619 | and assign them to the <tt class="member">tp_methods</tt> slot: | |
620 | ||
621 | <P> | |
622 | <div class="verbatim"><pre> | |
623 | Noddy_methods, /* tp_methods */ | |
624 | </pre></div> | |
625 | ||
626 | <P> | |
627 | Note that we used the <tt class="constant">METH_NOARGS</tt> flag to indicate that the | |
628 | method is passed no arguments. | |
629 | ||
630 | <P> | |
631 | Finally, we'll make our type usable as a base class. We've written | |
632 | our methods carefully so far so that they don't make any assumptions | |
633 | about the type of the object being created or used, so all we need to | |
634 | do is to add the <tt class="constant">Py_TPFLAGS_BASETYPE</tt> to our class flag | |
635 | definition: | |
636 | ||
637 | <P> | |
638 | <div class="verbatim"><pre> | |
639 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ | |
640 | </pre></div> | |
641 | ||
642 | <P> | |
643 | We rename <tt class="cfunction">initnoddy()</tt> to <tt class="cfunction">initnoddy2()</tt> | |
644 | and update the module name passed to <tt class="cfunction">Py_InitModule3()</tt>. | |
645 | ||
646 | <P> | |
647 | Finally, we update our <span class="file">setup.py</span> file to build the new module: | |
648 | ||
649 | <P> | |
650 | <div class="verbatim"><pre> | |
651 | from distutils.core import setup, Extension | |
652 | setup(name="noddy", version="1.0", | |
653 | ext_modules=[ | |
654 | Extension("noddy", ["noddy.c"]), | |
655 | Extension("noddy2", ["noddy2.c"]), | |
656 | ]) | |
657 | </pre></div> | |
658 | ||
659 | <P> | |
660 | <BR><HR><H4>Footnotes</H4> | |
661 | <DL> | |
662 | <DT><A NAME="foot1098">... object</A><A | |
663 | HREF="node22.html#tex2html5"><SUP>2.1</SUP></A></DT> | |
664 | <DD>This is | |
665 | true when we know that the object is a basic type, like a string or | |
666 | a float | |
667 | ||
668 | </DD> | |
669 | <DT><A NAME="foot1418">... supported</A><A | |
670 | HREF="node22.html#tex2html6"><SUP>2.2</SUP></A></DT> | |
671 | <DD>We relied | |
672 | on this in the <tt class="member">tp_dealloc</tt> handler in this example, because | |
673 | our type doesn't support garbage collection. Even if a type supports | |
674 | garbage collection, there are calls that can be made to ``untrack'' | |
675 | the object from garbage collection, however, these calls are | |
676 | advanced and not covered here. | |
677 | ||
678 | </DD> | |
679 | </DL> | |
680 | <DIV CLASS="navigation"> | |
681 | <div class='online-navigation'> | |
682 | <p></p><hr /> | |
683 | <table align="center" width="100%" cellpadding="0" cellspacing="2"> | |
684 | <tr> | |
685 | <td class='online-navigation'><a rel="prev" title="2.1 The Basics" | |
686 | href="dnt-basics.html"><img src='../icons/previous.png' | |
687 | border='0' height='32' alt='Previous Page' width='32' /></A></td> | |
688 | <td class='online-navigation'><a rel="parent" title="2.1 The Basics" | |
689 | href="dnt-basics.html"><img src='../icons/up.png' | |
690 | border='0' height='32' alt='Up One Level' width='32' /></A></td> | |
691 | <td class='online-navigation'><a rel="next" title="2.1.2 Providing finer control" | |
692 | href="node23.html"><img src='../icons/next.png' | |
693 | border='0' height='32' alt='Next Page' width='32' /></A></td> | |
694 | <td align="center" width="100%">Extending and Embedding the Python Interpreter</td> | |
695 | <td class='online-navigation'><a rel="contents" title="Table of Contents" | |
696 | href="contents.html"><img src='../icons/contents.png' | |
697 | border='0' height='32' alt='Contents' width='32' /></A></td> | |
698 | <td class='online-navigation'><img src='../icons/blank.png' | |
699 | border='0' height='32' alt='' width='32' /></td> | |
700 | <td class='online-navigation'><img src='../icons/blank.png' | |
701 | border='0' height='32' alt='' width='32' /></td> | |
702 | </tr></table> | |
703 | <div class='online-navigation'> | |
704 | <b class="navlabel">Previous:</b> | |
705 | <a class="sectref" rel="prev" href="dnt-basics.html">2.1 The Basics</A> | |
706 | <b class="navlabel">Up:</b> | |
707 | <a class="sectref" rel="parent" href="dnt-basics.html">2.1 The Basics</A> | |
708 | <b class="navlabel">Next:</b> | |
709 | <a class="sectref" rel="next" href="node23.html">2.1.2 Providing finer control</A> | |
710 | </div> | |
711 | </div> | |
712 | <hr /> | |
713 | <span class="release-info">Release 2.4.2, documentation updated on 28 September 2005.</span> | |
714 | </DIV> | |
715 | <!--End of Navigation Panel--> | |
716 | <ADDRESS> | |
717 | See <i><a href="about.html">About this document...</a></i> for information on suggesting changes. | |
718 | </ADDRESS> | |
719 | </BODY> | |
720 | </HTML> |