mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	Started updating information about defining attributes on types.
There's still a long way to go, but we're starting to see some real content in the docs.
This commit is contained in:
		
							parent
							
								
									bdcb1c4597
								
							
						
					
					
						commit
						0ffd14c9ea
					
				
					 1 changed files with 171 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -453,8 +453,7 @@ static PyObject *
 | 
			
		|||
newdatatype_str(newdatatypeobject * obj)
 | 
			
		||||
{
 | 
			
		||||
    return PyString_FromFormat("Stringified_newdatatype{{size:\%d}}",
 | 
			
		||||
        obj->obj_UnderlyingDatatypePtr->size
 | 
			
		||||
        );
 | 
			
		||||
                               obj->obj_UnderlyingDatatypePtr->size);
 | 
			
		||||
}
 | 
			
		||||
\end{verbatim}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -494,12 +493,175 @@ newdatatype_print(newdatatypeobject *obj, FILE *fp, int flags)
 | 
			
		|||
 | 
			
		||||
\subsection{Attribute Management Functions}
 | 
			
		||||
 | 
			
		||||
For every object which can support attributes, the corresponding type
 | 
			
		||||
must provide the functions that control how the attributes are
 | 
			
		||||
resolved.  There needs to be a function which can retrieve attributes
 | 
			
		||||
(if any are defined), and another to set attributes (if setting
 | 
			
		||||
attributes is allowed).  Removing an attribute is a special case, for
 | 
			
		||||
which the new value passed to the handler is \NULL.
 | 
			
		||||
 | 
			
		||||
Python supports two pairs of attribute handlers; a type that supports
 | 
			
		||||
attributes only needs to implement the functions for one pair.  The
 | 
			
		||||
difference is that one pair takes the name of the attribute as a
 | 
			
		||||
\ctype{char*}, while the other accepts a \ctype{PyObject*}.  Each type
 | 
			
		||||
can use whichever pair makes more sense for the implementation's
 | 
			
		||||
convenience.
 | 
			
		||||
 | 
			
		||||
\begin{verbatim}
 | 
			
		||||
    getattrfunc tp_getattr;
 | 
			
		||||
    getattrfunc  tp_getattr;        /* char * version */
 | 
			
		||||
    setattrfunc  tp_setattr;
 | 
			
		||||
    /* ... */
 | 
			
		||||
    getattrofunc tp_getattrofunc;   /* PyObject * version */
 | 
			
		||||
    setattrofunc tp_setattrofunc;
 | 
			
		||||
\end{verbatim}
 | 
			
		||||
 | 
			
		||||
The \member{tp_getattr} handle is called when the object requires an
 | 
			
		||||
If accessing attributes of an object is always a simple operation
 | 
			
		||||
(this will be explained shortly), there are generic implementations
 | 
			
		||||
which can be used to provide the \ctype{PyObject*} version of the
 | 
			
		||||
attribute management functions.  The actual need for type-specific
 | 
			
		||||
attribute handlers almost completely disappeared starting with Python
 | 
			
		||||
2.2, though there are many examples which have not been updated to use
 | 
			
		||||
some of the new generic mechanism that is available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\subsubsection{Generic Attribute Management}
 | 
			
		||||
 | 
			
		||||
\versionadded{2.2}
 | 
			
		||||
 | 
			
		||||
Most extension types only use \emph{simple} attributes.  So, what
 | 
			
		||||
makes the attributes simple?  There are only a couple of conditions
 | 
			
		||||
that must be met:
 | 
			
		||||
 | 
			
		||||
\begin{enumerate}
 | 
			
		||||
  \item   The name of the attributes must be known when
 | 
			
		||||
          \cfunction{PyType_Ready()} is called.
 | 
			
		||||
 | 
			
		||||
  \item   No special processing is need to record that an attribute
 | 
			
		||||
          was looked up or set, nor do actions need to be taken based
 | 
			
		||||
          on the value.
 | 
			
		||||
\end{enumerate}
 | 
			
		||||
 | 
			
		||||
Note that this list does not place any restrictions on the values of
 | 
			
		||||
the attributes, when the values are computed, or how relevant data is
 | 
			
		||||
stored.
 | 
			
		||||
 | 
			
		||||
When \cfunction{PyType_Ready()} is called, it uses three tables
 | 
			
		||||
referenced by the type object to create \emph{descriptors} which are
 | 
			
		||||
placed in the dictionary of the type object.  Each descriptor controls
 | 
			
		||||
access to one attribute of the instance object.  Each of the tables is
 | 
			
		||||
optional; if all three are \NULL, instances of the type will only have
 | 
			
		||||
attributes that are inherited from their base type, and should leave
 | 
			
		||||
the \member{tp_getattro} and \member{tp_setattro} fields \NULL{} as
 | 
			
		||||
well, allowing the base type to handle attributes.
 | 
			
		||||
 | 
			
		||||
The tables are declared as three fields of the type object:
 | 
			
		||||
 | 
			
		||||
\begin{verbatim}
 | 
			
		||||
    struct PyMethodDef *tp_methods;
 | 
			
		||||
    struct PyMemberDef *tp_members;
 | 
			
		||||
    struct PyGetSetDef *tp_getset;
 | 
			
		||||
\end{verbatim}
 | 
			
		||||
 | 
			
		||||
If \member{tp_methods} is not \NULL, it must refer to an array of
 | 
			
		||||
\ctype{PyMethodDef} structures.  Each entry in the table is an
 | 
			
		||||
instance of this structure:
 | 
			
		||||
 | 
			
		||||
\begin{verbatim}
 | 
			
		||||
typedef struct PyMethodDef {
 | 
			
		||||
    char        *ml_name;       /* method name */
 | 
			
		||||
    PyCFunction  ml_meth;       /* implementation function */
 | 
			
		||||
    int	         ml_flags;      /* flags */
 | 
			
		||||
    char        *ml_doc;        /* docstring */
 | 
			
		||||
} PyMethodDef;
 | 
			
		||||
\end{verbatim}
 | 
			
		||||
 | 
			
		||||
One entry should be defined for each method provided by the type; no
 | 
			
		||||
entries are needed for methods inherited from a base type.  One
 | 
			
		||||
additional entry is needed at the end; it is a sentinel that marks the
 | 
			
		||||
end of the array.  The \member{ml_name} field of the sentinel must be
 | 
			
		||||
\NULL.
 | 
			
		||||
 | 
			
		||||
XXX Need to refer to some unified discussion of the structure fields,
 | 
			
		||||
shared with the next section.
 | 
			
		||||
 | 
			
		||||
The second table is used to define attributes which map directly to
 | 
			
		||||
data stored in the instance.  A variety of primitive C types are
 | 
			
		||||
supported, and access may be read-only or read-write.  The structures
 | 
			
		||||
in the table are defined as:
 | 
			
		||||
 | 
			
		||||
\begin{verbatim}
 | 
			
		||||
typedef struct PyMemberDef {
 | 
			
		||||
    char *name;
 | 
			
		||||
    int   type;
 | 
			
		||||
    int   offset;
 | 
			
		||||
    int   flags;
 | 
			
		||||
    char *doc;
 | 
			
		||||
} PyMemberDef;
 | 
			
		||||
\end{verbatim}
 | 
			
		||||
 | 
			
		||||
For each entry in the table, a descriptor will be constructed and
 | 
			
		||||
added to the type which will be able to extract a value from the
 | 
			
		||||
instance structure.  The \member{type} field should contain one of the
 | 
			
		||||
type codes defined in the \file{structmember.h} header; the value will
 | 
			
		||||
be used to determine how to convert Python values to and from C
 | 
			
		||||
values.  The \member{flags} field is used to store flags which control
 | 
			
		||||
how the attribute can be accessed.
 | 
			
		||||
 | 
			
		||||
XXX Need to move some of this to a shared section!
 | 
			
		||||
 | 
			
		||||
The following flag constants are defined in \file{structmember.h};
 | 
			
		||||
they may be combined using bitwise-OR.
 | 
			
		||||
 | 
			
		||||
\begin{tableii}{l|l}{constant}{Constant}{Meaning}
 | 
			
		||||
  \lineii{READONLY \ttindex{READONLY}}
 | 
			
		||||
         {Never writable.}
 | 
			
		||||
  \lineii{RO \ttindex{RO}}
 | 
			
		||||
         {Shorthand for \constant{READONLY}.}
 | 
			
		||||
  \lineii{READ_RESTRICTED \ttindex{READ_RESTRICTED}}
 | 
			
		||||
         {Not readable in restricted mode.}
 | 
			
		||||
  \lineii{WRITE_RESTRICTED \ttindex{WRITE_RESTRICTED}}
 | 
			
		||||
         {Not writable in restricted mode.}
 | 
			
		||||
  \lineii{RESTRICTED \ttindex{RESTRICTED}}
 | 
			
		||||
         {Not readable or writable in restricted mode.}
 | 
			
		||||
\end{tableii}
 | 
			
		||||
 | 
			
		||||
An interesting advantage of using the \member{tp_members} table to
 | 
			
		||||
build descriptors that are used at runtime is that any attribute
 | 
			
		||||
defined this way can have an associated docstring simply by providing
 | 
			
		||||
the text in the table.  An application can use the introspection API
 | 
			
		||||
to retrieve the descriptor from the class object, and get the
 | 
			
		||||
docstring using its \member{__doc__} attribute.
 | 
			
		||||
 | 
			
		||||
As with the \member{tp_methods} table, a sentinel entry with a
 | 
			
		||||
\member{name} value of \NULL{} is required.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
% XXX Descriptors need to be explained in more detail somewhere, but
 | 
			
		||||
% not here.
 | 
			
		||||
%
 | 
			
		||||
% Descriptor objects have two handler functions which correspond to
 | 
			
		||||
% the \member{tp_getattro} and \member{tp_setattro} handlers.  The
 | 
			
		||||
% \method{__get__()} handler is a function which is passed the
 | 
			
		||||
% descriptor, instance, and type objects, and returns the value of the
 | 
			
		||||
% attribute, or it returns \NULL{} and sets an exception.  The
 | 
			
		||||
% \method{__set__()} handler is passed the descriptor, instance, type,
 | 
			
		||||
% and new value;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\subsubsection{Type-specific Attribute Management}
 | 
			
		||||
 | 
			
		||||
For simplicity, only the \ctype{char*} version will be demonstrated
 | 
			
		||||
here; the type of the name parameter is the only difference between
 | 
			
		||||
the \ctype{char*} and \ctype{PyObject*} flavors of the interface.
 | 
			
		||||
This example effectively does the same thing as the generic example
 | 
			
		||||
above, but does not use the generic support added in Python 2.2.  The
 | 
			
		||||
value in showing this is two-fold: it demonstrates how basic attribute
 | 
			
		||||
management can be done in a way that is portable to older versions of
 | 
			
		||||
Python, and explains how the handler functions are called, so that if
 | 
			
		||||
you do need to extend their functionality, you'll understand what
 | 
			
		||||
needs to be done.
 | 
			
		||||
 | 
			
		||||
The \member{tp_getattr} handler 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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +669,9 @@ 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.
 | 
			
		||||
function that returns the result of a lookup in that table.  The
 | 
			
		||||
method table uses the same structure as the \member{tp_methods} field
 | 
			
		||||
of the type object.
 | 
			
		||||
 | 
			
		||||
Here is an example:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue