trouble with the C API

Thomas Wouters thomas at xs4all.net
Mon Aug 21 18:57:14 EDT 2000


On Mon, Aug 21, 2000 at 07:21:02PM +0000, Stefan Seefeld wrote:

> doesn't each object have two types, one static type, determined by the
> type object it refers to and one dynamic type, given through the presence
> (at a given point in time !) of a particular method or attribute ?
> Why this ambiguity ?

You're confusing the C extention types with Python objects. The C PyObject*
system is flexible, but not as flexible as Python classes. (Well, you can
make them that flexible, but not easily so. And you pay a heavy price in
performance, too.)

The C objects only have 1 type, the static type. It's the type that defines
what operations can be performed on an object, and how they are performed.
The PyObject itself needn't be large: it usually consists of some
bookkeeping info (like the reference count, the object size and a pointer to
the associated type object) and the data it is really storing (usually an
array of some sort.) It doesn't record anything else about its behaviour.

The real code is in the PyType object for this type of object. It defines
everything about the PyObject. For a complete list of how and what, see
Includes/object.h in the Python source directory (or
/usr/local/include/python1.5/object.h or so), but suffice it to say that for
each operation on an object, there is one, or sometimes more than one,
associated 'method' -- a pointer to a C function.

For instance, The PyTypeObject struct has a struct member called
'tp_as_mapping', which is a pointer to a struct 'PyMappingMethods', which
holds function-pointers for all 'mapping-like' operations:

typedef struct {
        inquiry mp_length;
        binaryfunc mp_subscript;
        objobjargproc mp_ass_subscript;
} PyMappingMethods;

Respectively, getting the length (len(m)) of the mapping, retrieving an item
(m[x]) and storing an item (m[x] = y). (inquiry, binaryfunc and
objobjargproc are typedefs to function-pointers of the right type. More
details in Include/object.h ;)

The Python API defines a large number of functions that take one or more
PyObject pointers (and possibly other data), and call the appropriate
function for that object. (You want to use the API instead of calling those
function-pointers directly, for several reasons: if you are mixing types,
which is common, the arguments might need to be 'coerced' into a compatible
type. And if one or more of the objects is an 'instance' type, that is, a
Python class, it needs special treatment: you want to try and call the
__hook__ methods specific to the operation.)

So that is what the 'higher level' API gives: the 'treat-me-as-X' API.
PyMapping_GetItem(), for instance, differs from PySequence_GetItem() in that
the 'key' in PyMapping_GetItem() is just a PyObject, whereas for
PySequence_GetItem() it is an int, *not* a PyIntObject.

However, assides from this API you also need a type-specific API, especially
for the builtin-types, which are used a lot (if not in extention code then
at least in Python C code itself: a lot of the Python internals use normal
Python types!) If you want a new dictionary, you call PyDict_New(). If you
know you have a PyDict, and you want to insert an item, you call
PyDict_SetItem(). This differs from going through the Mapping 'protocol' in
that you have direct control over what you are inserting, and do not have to
worry about coercion or any of that. On the other hand, it *will* fail if
the type is not a PyDict.

So, in general, if you do not know what type of object you are talking
about, use the 'general' API, which is found in Include/abstract.h (and
implemented in Objects/abstract.c.) There are four main groups of API calls:
PyNumber_*, for number-like operations; PySequence_*, for Sequence-like
operations; PyMapping_*, for mapping-like operations; and PyObject_*, for
dealing with objects in general. The PyObject_* API is the most general one,
since it will usually call one of the other three types of API to do the
actual work, after it has found out what kind of object you want it to work
on.

If you are dealing directly with a C type, and you know what it is (mostly
because you created it yourself, or you performed the appropriate
Py<object>_Check()) it is a lot more efficient and readable to use the
type-specific methods. But beware if you later wish to change the acceptable
types, for instance ! The type-specific APIs are explained in detail in the
API reference.

If you need more info, and can't find it in the Extending and Embedding
manual or the API reference, try browsing through Include/abstract.h and
Include/object.h. They contain a lot of useful comments. Of course, you can
ask here too :-)

-- 
Thomas Wouters <thomas at xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!




More information about the Python-list mailing list