|
Python supports a variety of abstract `protocols;' the specific interfaces provided
to use these interfaces are documented in the Python/C API Reference Manual in the chapter ``Abstract Objects Layer.''
A number of these abstract interfaces were defined early in the development of the Python
implementation. In particular, the number, mapping, and sequence protocols have been part of
Python since the beginning. Other protocols have been added over time. For protocols which
depend on several handler routines from the type implementation, the older protocols have been
defined as optional blocks of handlers referenced by the type object. For newer protocols
there are additional slots in the main type object, with a flag bit being set to indicate that
the slots are present and should be checked by the interpreter. (The flag bit does not
indicate that the slot values are non-NULL. The flag may be set to
indicate the presense of a slot, but a slot may still be unfilled.)
PyNumberMethods tp_as_number;
PySequenceMethods tp_as_sequence;
PyMappingMethods tp_as_mapping;
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 PyNumberMethods,
PySequenceMethods, or 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 Objects directory of the
Python source distribution.
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:
static long
newdatatype_hash(newdatatypeobject *obj)
{
long result;
result = obj->obj_UnderlyingDatatypePtr->size;
result = result * 3;
return result;
}
This function is called when an instance of your datatype is "called", for
example, if obj1 is an instance of your datatype and the Python script contains obj1('hello'),
the tp_call handler is invoked.
This function takes three arguments:
- arg1 is the instance of the datatype which is the subject of the call. If the
call is
obj1('hello'), then arg1 is obj1.
- arg2 is a tuple containing the arguments to the call. You can use PyArg_ParseTuple() to extract the arguments.
- arg3 is a dictionary of keyword arguments that were passed. If this is non-NULL and you support keyword arguments, use PyArg_ParseTupleAndKeywords()
to extract the arguments. If you do not want to support keyword arguments and this is non-NULL, raise a TypeError with a message
saying that keyword arguments are not supported.
Here is a desultory example of the implementation of the call function.
/* 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;
if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
return NULL;
}
result = PyString_FromFormat(
"Returning -- value: [\%d] arg1: [\%s] arg2: [\%s] arg3: [\%s]\n",
obj->obj_UnderlyingDatatypePtr->size,
arg1, arg2, arg3);
printf("\%s", PyString_AS_STRING(result));
return result;
}
XXX some fields need to be added here...
/* Added in release 2.2 */
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
These functions provide support for the iterator protocol. Any object which wishes to
support iteration over its contents (which may be generated during iteration) must implement
the tp_iter handler. Objects which are returned by a tp_iter handler
must implement both the tp_iter and tp_iternext handlers. Both
handlers take exactly one parameter, the instance for which they are being called, and return
a new reference. In the case of an error, they should set an exception and return NULL.
For an object which represents an iterable collection, the tp_iter handler
must return an iterator object. The iterator object is responsible for maintaining the state
of the iteration. For collections which can support multiple iterators which do not interfere
with each other (as lists and tuples do), a new iterator should be created and returned.
Objects which can only be iterated over once (usually due to side effects of iteration) should
implement this handler by returning a new reference to themselves, and should also implement
the tp_iternext handler. File objects are an example of such an iterator.
Iterator objects should implement both handlers. The tp_iter handler should
return a new reference to the iterator (this is the same as the tp_iter handler
for objects which can only be iterated over destructively). The tp_iternext
handler should return a new reference to the next object in the iteration if there is one. If
the iteration has reached the end, it may return NULL without
setting an exception or it may set StopIteration; avoiding the
exception can yield slightly better performance. If an actual error occurs, it should set an
exception and return NULL.
|