[Python-checkins] CVS: python/dist/src/Doc/ext ext.tex,1.102,1.103

Fred L. Drake fdrake@users.sourceforge.net
Wed, 15 Aug 2001 12:07:20 -0700


Update of /cvsroot/python/python/dist/src/Doc/ext
In directory usw-pr-cvs1:/tmp/cvs-serv19243/ext

Modified Files:
	ext.tex 
Log Message:

A large contribution from Dave Kuhlman describing what each of the slots
in the type object is used for, for many of the more commonly used slots.
Thanks!

(But there is still a lot more to write on this topic.)
Markup and organizational changes by your friendly neighborhood
documentation czar.


Index: ext.tex
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/ext.tex,v
retrieving revision 1.102
retrieving revision 1.103
diff -C2 -d -r1.102 -r1.103
*** ext.tex	2001/08/04 01:58:36	1.102
--- ext.tex	2001/08/15 19:07:18	1.103
***************
*** 1751,1754 ****
--- 1751,1755 ----
          \label{defining-new-types}}
  \sectionauthor{Michael Hudson}{mwh21@cam.ac.uk}
+ \sectionauthor{Dave Kuhlman}{dkuhlman@rexx.com}
  
  As mentioned in the last chapter, Python allows the writer of an
***************
*** 2066,2069 ****
--- 2067,2071 ----
  That wasn't so hard, was it?
  
+ 
  \section{Type Methods
           \label{dnt-type-methods}}
***************
*** 2112,2115 ****
--- 2114,2118 ----
      char *tp_doc; /* Documentation string */
  
+     /* Assigned meaning in release 2.0 */
      /* call function for all accessible objects */
      traverseproc tp_traverse;
***************
*** 2118,2121 ****
--- 2121,2125 ----
      inquiry tp_clear;
  
+     /* Assigned meaning in release 2.1 */
      /* rich comparisons */
      richcmpfunc tp_richcompare;
***************
*** 2124,2127 ****
--- 2128,2153 ----
      long tp_weaklistoffset;
  
+     /* Added in release 2.2 */
+     /* Iterators */
+     getiterfunc tp_iter;
+     iternextfunc tp_iternext;
+ 
+     /* Attribute descriptor and subclassing stuff */
+     struct PyMethodDef *tp_methods;
+     struct memberlist *tp_members;
+     struct getsetlist *tp_getset;
+     struct _typeobject *tp_base;
+     PyObject *tp_dict;
+     descrgetfunc tp_descr_get;
+     descrsetfunc tp_descr_set;
+     long tp_dictoffset;
+     initproc tp_init;
+     allocfunc tp_alloc;
+     newfunc tp_new;
+     destructor tp_free; /* Low-level free-memory routine */
+     PyObject *tp_bases;
+     PyObject *tp_mro; /* method resolution order */
+     PyObject *tp_defined;
+ 
  } PyTypeObject;
  \end{verbatim}
***************
*** 2131,2136 ****
  will only implement a handful of these.
  
! As you probably expect by now, I'm going to go over this line-by-line,
! saying a word about each field as we get to it.
  
  \begin{verbatim}
--- 2157,2168 ----
  will only implement a handful of these.
  
! As you probably expect by now, we're going to go over this and give
! more information about the various handlers.  We won't go in the order
! they are defined in the structure, because there is a lot of
! historical baggage that impacts the ordering of the fields; be sure
! your type initializaion keeps the fields in the right order!  It's
! often easiest to find an example that includes all the fields you need
! (even if they're initialized to \code{0}) and then change the values
! to suit your new type.
  
  \begin{verbatim}
***************
*** 2152,2170 ****
  later.
  
! Now we come to the basic type methods - the ones most extension types
  will implement.
  
  \begin{verbatim}
      destructor tp_dealloc;
      printfunc tp_print;
      getattrfunc tp_getattr;
      setattrfunc tp_setattr;
      cmpfunc tp_compare;
-     reprfunc tp_repr;
  \end{verbatim}
  
  
! %\section{Attributes \& Methods
! %    \label{dnt-attrs-and-meths}}
  
  
--- 2184,2549 ----
  later.
  
! \begin{verbatim}
!     char *tp_doc;
! \end{verbatim}
! 
! Here you can put a string (or its address) that you want returned when
! the Python script references \code{obj.__doc__} to retrieve the
! docstring.
!    
! Now we come to the basic type methods---the ones most extension types
  will implement.
  
+ 
+ \subsection{Finalization and De-allocation}
+ 
  \begin{verbatim}
      destructor tp_dealloc;
+ \end{verbatim}
+ 
+ This function is called when the reference count of the instance of
+ your type is reduced to zero and the Python interpreter wants to
+ reclaim it.  If your type has memory to free or other clean-up to
+ perform, put it here.  The object itself needs to be freed here as
+ well.  Here is an example of this function:
+ 
+ \begin{verbatim}
+ static void
+ newdatatype_dealloc(newdatatypeobject * obj)
+ {
+     free(obj->obj_UnderlyingDatatypePtr);
+     PyObject_DEL(obj);
+ }
+ \end{verbatim}
+ 
+ 
+ \subsection{Object Representation}
+ 
+ In Python, there are three ways to generate a textual representation
+ of an object: the \function{repr()}\bifuncindex{repr} function (or
+ equivalent backtick syntax), the \function{str()}\bifuncindex{str}
+ function, and the \keyword{print} statement.  For most objects, the
+ \keyword{print} statement is equivalent to the \function{str()}
+ function, but it is possible to special-case printing to a
+ \ctype{FILE*} if necessary; this should only be done if efficiency is
+ identified as a problem and profiling suggests that creating a
+ temporary string object to be written to a file is too expensive.
+ 
+ These handlers are all optional, and most types at most need to
+ implement the \member{tp_str} and \member{tp_repr} handlers.
+ 
+ \begin{verbatim}
+     reprfunc tp_repr;
+     reprfunc tp_str;
      printfunc tp_print;
+ \end{verbatim}
+ 
+ The \member{tp_repr} handler should return a string object containing
+ a representation of the instance for which it is called.  Here is a
+ simple example:
+ 
+ \begin{verbatim}
+ static PyObject *
+ newdatatype_repr(newdatatypeobject * obj)
+ {
+     char buf[4096];
+     sprintf(buf, "Repr-ified_newdatatype{{size:%d}}",
+             obj->obj_UnderlyingDatatypePtr->size);
+     return PyString_FromString(buf);
+ }
+ \end{verbatim}
+ 
+ If no \member{tp_repr} handler is specified, the interpreter will
+ supply a representation that uses the type's \member{tp_name} and a
+ uniquely-identifying value for the object.
+ 
+ The \member{tp_str} handler is to \function{str()} what the
+ \member{tp_repr} handler described above is to \function{repr()}; that
+ is, it is called when Python code calls \function{str()} on an
+ instance of your object.  It's implementation is very similar to the
+ \member{tp_repr} function, but the resulting string is intended to be
+ human consumption.  It \member{tp_str} is not specified, the
+ \member{tp_repr} handler is used instead.
+ 
+ Here is a simple example:
+ 
+ \begin{verbatim}
+ static PyObject *
+ newdatatype_str(newdatatypeobject * obj)
+ {
+     PyObject *pyString;
+     char buf[4096];
+     sprintf(buf, "Stringified_newdatatype{{size:%d}}",
+         obj->obj_UnderlyingDatatypePtr->size
+         );
+     pyString = PyString_FromString(buf);
+     return pyString;
+ }
+ \end{verbatim}
+ 
+ The print function will be called whenever Python needs to "print" an
+ instance of the type.  For example, if 'node' is an instance of type
+ TreeNode, then the print function is called when Python code calls:
+ 
+ \begin{verbatim}
+ print node
+ \end{verbatim}
+ 
+ There is a flags argument and one flag, \constant{Py_PRINT_RAW}, and
+ it suggests that you print without string quotes and possibly without
+ interpreting escape sequences.
+ 
+ The print function receives a file object as an argument. You will
+ likely want to write to that file object.
+ 
+ Here is a sampe print function:
+ 
+ \begin{verbatim}
+ static int
+ newdatatype_print(newdatatypeobject *obj, FILE *fp, int flags)
+ {
+     if (flags & Py_PRINT_RAW) {
+         fprintf(fp, "<{newdatatype object--size: %d}>",
+                 obj->obj_UnderlyingDatatypePtr->size);
+     }
+     else {
+         fprintf(fp, "\"<{newdatatype object--size: %d}>\"",
+                 obj->obj_UnderlyingDatatypePtr->size);
+     }
+     return 0;
+ }
+ \end{verbatim}
+ 
+ 
+ \subsection{Attribute Management Functions}
+ 
+ \begin{verbatim}
      getattrfunc tp_getattr;
      setattrfunc tp_setattr;
+ \end{verbatim}
+ 
+ The \member{tp_getattr} handle is called when the object requires an
+ attribute look-up.  It is called in the same situations where the
+ \method{__getattr__()} method of a class would be called.
+ 
+ A likely way to handle this is (1) to implement a set of functions
+ (such as \cfunction{newdatatype_getSize()} and
+ \cfunction{newdatatype_setSize()} in the example below), (2) provide a
+ method table listing these functions, and (3) provide a getattr
+ function that returns the result of a lookup in that table.
+ 
+ Here is an example:
+ 
+ \begin{verbatim}
+ static PyMethodDef newdatatype_methods[] = {
+     {"getSize", (PyCFunction)newdatatype_getSize, METH_VARARGS},
+     {"setSize", (PyCFunction)newdatatype_setSize, METH_VARARGS},
+     {NULL,      NULL}           /* sentinel */
+ };
+ 
+ static PyObject *
+ newdatatype_getattr(newdatatypeobject *obj, char *name)
+ {
+     return Py_FindMethod(newdatatype_methods, (PyObject *)obj, name);
+ }
+ \end{verbatim}
+ 
+ The \member{tp_setattr} handler is called when the
+ \method{__setattr__()} or \method{__delattr__()} method of a class
+ instance would be called.  When an attribute should be deleted, the
+ third parameter will be \NULL.  Here is an example that simply raises
+ an exception; if this were really all you wanted, the
+ \member{tp_setattr} handler should be set to \NULL.
+    
+ \begin{verbatim}
+ static int
+ newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v)
+ {
+     char buf[1024];
+     sprintf(buf, "Set attribute not supported for attribute %s", name);
+     PyErr_SetString(PyExc_RuntimeError, buf);
+     return -1;
+ }
+ \end{verbatim}
+ 
+ 
+ \subsection{Object Comparison}
+ 
+ \begin{verbatim}
      cmpfunc tp_compare;
  \end{verbatim}
  
+ The \member{tp_compare} handler is called when comparisons are needed
+ are the object does not implement the specific rich comparison method
+ which matches the requested comparison.  (It is always used if defined
+ and the \cfunction{PyObject_Compare()} or \cfunction{PyObject_Cmp()}
+ functions are used, or if \function{cmp()} is used from Python.)
+ It is analogous to the \method{__cmp__()} method.  This function
+ should return a negative integer if \var{obj1} is less than
+ \var{obj2}, \code{0} if they are equal, and a positive integer if
+ \var{obj1} is greater than
+ \var{obj2}.
  
! Here is a sample implementation:
! 
! \begin{verbatim}
! static int
! newdatatype_compare(newdatatypeobject * obj1, newdatatypeobject * obj2)
! {
!     long result;
!  
!     if (obj1->obj_UnderlyingDatatypePtr->size <
!         obj2->obj_UnderlyingDatatypePtr->size) {
!         result = -1;
!     }
!     else if (obj1->obj_UnderlyingDatatypePtr->size >
!              obj2->obj_UnderlyingDatatypePtr->size) {
!         result = 1;
!     }
!     else {
!         result = 0;
!     }
!     return result;
! }
! \end{verbatim}
! 
! 
! \subsection{Abstract Protocol Support}
! 
! \begin{verbatim}
!   tp_as_number;
!   tp_as_sequence;
!   tp_as_mapping;
! \end{verbatim}
! 
! If you wish your object to be able to act like a number, a sequence,
! or a mapping object, then you place the address of a structure that
! implements the C type \ctype{PyNumberMethods},
! \ctype{PySequenceMethods}, or \ctype{PyMappingMethods}, respectively.
! It is up to you to fill in this structure with appropriate values. You
! can find examples of the use of each of these in the \file{Objects}
! directory of the Python source distribution.
! 
! 
! \begin{verbatim}
!     hashfunc tp_hash;
! \end{verbatim}
! 
! This function, if you choose to provide it, should return a hash
! number for an instance of your datatype. Here is a moderately
! pointless example:
! 
! \begin{verbatim}
! static long
! newdatatype_hash(newdatatypeobject *obj)
! {
!     long result;
!     result = obj->obj_UnderlyingDatatypePtr->size;
!     result = result * 3;
!     return result;
! }
! \end{verbatim}
! 
! \begin{verbatim}
!     ternaryfunc tp_call;
! \end{verbatim}
! 
! This function is called when an instance of your datatype is "called",
! for example, if \code{obj1} is an instance of your datatype and the Python
! script contains \code{obj1('hello')}, the \member{tp_call} handler is
! invoked.
! 
! This function takes three arguments:
! 
! \begin{enumerate}
!   \item
!     \var{arg1} is the instance of the datatype which is the subject of
!     the call. If the call is \code{obj1('hello')}, then \var{arg1} is
!     \code{obj1}.
! 
!   \item
!     \var{arg2} is a tuple containing the arguments to the call.  You
!     can use \cfunction{PyArg_ParseTuple()} to extract the arguments.
! 
!   \item
!     \var{arg3} is a dictionary of keyword arguments that were passed.
!     If this is non-\NULL{} and you support keyword arguments, use
!     \cfunction{PyArg_ParseTupleAndKeywords()} to extract the
!     arguments.  If you do not want to support keyword arguments and
!     this is non-\NULL, raise a \exception{TypeError} with a message
!     saying that keyword arguments are not supported.
! \end{enumerate}
!        
! Here is a desultory example of the implementation of call function.
! 
! \begin{verbatim}
! /* Implement the call function.
!  *    obj1 is the instance receiving the call.
!  *    obj2 is a tuple containing the arguments to the call, in this
!  *         case 3 strings.
!  */
! static PyObject *
! newdatatype_call(newdatatypeobject *obj, PyObject *args, PyObject *other)
! {
!     PyObject *result;
!     char *arg1;
!     char *arg2;
!     char *arg3;
!     char buf[4096];
!     if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
!         return NULL;
!     }
!     sprintf(buf,
!             "Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",
!             obj->obj_UnderlyingDatatypePtr->size,
!             arg1, arg2, arg3);
!     printf(buf);
!     return PyString_FromString(buf);
! }
! \end{verbatim}
! 
! 
! \subsection{More Suggestions}
! 
! Remember that you can omit most of these functions, in which case you
! provide \code{0} as a value.
! 
! In the \file{Objects} directory of the Python source distribution,
! there is a file \file{xxobject.c}, which is intended to be used as a
! template for the implementation of new types.  One useful strategy
! for implementing a new type is to copy and rename this file, then
! read the instructions at the top of it.
! 
! There are type definitions for each of the functions you must
! provide.  They are in \file{object.h} in the Python include
! directory that comes with the source distribution of Python.
! 
! In order to learn how to implement any specific method for your new
! datatype, do the following: Download and unpack the Python source
! distribution.  Go the the \file{Objects} directory, then search the
! C source files for \code{tp_} plus the function you want (for
! example, \code{tp_print} or \code{tp_compare}).  You will find
! examples of the function you want to implement.
! 
! When you need to verify that the type of an object is indeed the
! object you are implementing and if you use xxobject.c as an starting
! template for your implementation, then there is a macro defined for
! this purpose. The macro definition will look something like this:
! 
! \begin{verbatim}
! #define is_newdatatypeobject(v)  ((v)->ob_type == &Newdatatypetype)
! \end{verbatim}
! 
! And, a sample of its use might be something like the following:
! 
! \begin{verbatim}
!     if (!is_newdatatypeobject(objp1) {
!         PyErr_SetString(PyExc_TypeError, "arg #1 not a newdatatype");
!         return NULL;
!     }
! \end{verbatim}
! 
! %For a reasonably extensive example, from which most of the snippits
! %above were taken, see \file{newdatatype.c} and \file{newdatatype.h}.