Typing system vs. Java

Alex Martelli aleaxit at yahoo.com
Fri Aug 10 10:14:33 CEST 2001


"Courageous" <jkraska1 at san.rr.com> wrote in message
news:j207nt8cb1snjb7hib3k0c9d9ij1jn6vrq at 4ax.com...
    ...
> and C so easily comingle: they were meant to. Python extension
> writing is very easy, with the caveat that the moment you go from
> creating an extension module to wanting to be able to generate
> _constructable_ object instances of your native objects, you must
> go to the trouble of learning the subtleties of the reference counting
> system. Once you're past that, however, and all is cake.

The "subtleties of reference counting" aren't all that subtle,
even though it's probably worth using the Boost Python
Library to be able to forget them.  Consider, for example,
the C-extension version of my tiny elementary-list Python
module, i.e.:

def cons(a,b): return [a,b]
def car(alist): return alist[0]
def cdr(alist): return alist[1]
def setcar(alist, a): alis[0]=a
def setcdr(alist, b): alis[1]=b

All you need to transliterate this into C is the following
setup.py distutils-script:

from distutils.core import setup, Extension

setup (name = "elemlist",
       version = "1.0",
       maintainer = "Alex Martelli",
       maintainer_email = "aleax at aleax.it",
       description = "Sample Python module",

       ext_modules = [Extension('elemlist',sources=['elemlist.c'])]
)

and the elemlist.c code, of course (shorn of comments & docs):

#include "Python.h"
typedef struct {
    PyObject_HEAD
    PyObject *car, *cdr;
} cons_cell;
staticforward PyTypeObject cons_type;
#define carof(v) (((cons_cell*)(v))->car)
#define cdrof(v) (((cons_cell*)(v))->cdr)

static cons_cell*
cons_new(PyObject *car, PyObject *cdr)
{
    cons_cell *cons = PyObject_NEW(cons_cell, &cons_type);
    if(!cons) return 0;
    cons->car = car; Py_INCREF(car);
    cons->cdr = cdr; Py_INCREF(cdr);
    return cons;
}
static void
cons_dealloc(cons_cell* cons)
{
    Py_DECREF(carof(cons)); Py_DECREF(cdrof(cons));
    PyObject_DEL(cons);
}
static PyMethodDef no_methods[] = { {0, 0} };

statichere PyTypeObject cons_type = {
    PyObject_HEAD_INIT(0)
    0,                 /*ob_size*/
    "cons",            /*tp_name*/
    sizeof(cons_cell), /*tp_basicsize*/
    0,                 /*tp_itemsize*/
    /* methods */
    (destructor)cons_dealloc, /*tp_dealloc*/
    /* implied by ISO C: all zeros thereafter */
};

static PyObject*
cons(PyObject *self, PyObject *args)
{
    PyObject *car, *cdr;
    if(!PyArg_ParseTuple(args, "OO", &car, &cdr))
        return 0;
    return (PyObject*)cons_new(car, cdr);
}
static PyObject*
car(PyObject *self, PyObject *args)
{
    PyObject *cons;
    if(!PyArg_ParseTuple(args, "O!", &cons_type, &cons))
        return 0;
    Py_INCREF(carof(cons)); return carof(cons);
}
static PyObject*
cdr(PyObject *self, PyObject *args)
{
    PyObject *cons;
    if(!PyArg_ParseTuple(args, "O!", &cons_type, &cons))
        return 0;
    Py_INCREF(cdrof(cons)); return cdrof(cons);
}
static PyObject*
setcar(PyObject *self, PyObject *args)
{
    PyObject *cons, *car;
    if(!PyArg_ParseTuple(args, "O!O", &cons_type, &cons, &car))
        return 0;
    Py_INCREF(car);
    Py_DECREF(carof(cons));
    carof(cons) = car;
    Py_INCREF(Py_None); return Py_None;
}
static PyObject*
setcdr(PyObject *self, PyObject *args)
{
    PyObject *cons, *cdr;
    if(!PyArg_ParseTuple(args, "O!O", &cons_type, &cons, &cdr))
        return 0;
    Py_INCREF(cdr);
    Py_DECREF(cdrof(cons));
    cdrof(cons) = cdr;
    Py_INCREF(Py_None); return Py_None;
}
static PyMethodDef elemlist_methods[] = {
    {"cons",   cons,   METH_VARARGS},
    {"car",    car,    METH_VARARGS},
    {"cdr",    cdr,    METH_VARARGS},
    {"setcar", setcar, METH_VARARGS},
    {"setcdr", setcdr, METH_VARARGS},
    {0, 0}
};

void
initelemlist(void)
{
    PyObject *m = Py_InitModule("elemlist", elemlist_methods);
    cons_type.ob_type = &PyType_Type;
}

There -- not too bad, is it?  I've spelled out the INCREF and DECREF
just to show there aren't all that many subtleties.  I'd normally use
Py_BuildValue in lieu of the 4 occurrences of INCREF/return pairs,
so that would leave only two INCREF on construction, two DECREF
on destruction, and two occurrences of the rebinding-idiom e.g.:
    Py_INCREF(car);
    Py_DECREF(carof(cons));
    carof(cons) = car;
increment-RHS, decrement-LHS, copy the pointer.  Actually, I
don't think the subtlety is really needed here -- there has to be
one extra extant reference to the car object even in a case such
as setcar(x,car(x)), so even if the DECREF was done first it
would not destroy the object -- but one does get used to using
this pattern in all sorts of reference-counting systems, and in
some cases it can save one's skin:-).  But anyway, how hard can
it be, really, to learn about this idiom even if you've never used
a reference-counting system before...?

There ARE subtleties elsewhere, in that at first one has to look
up which API calls just borrow a reference versus which ones
do the increment.  There IS some logic to it however (albeit
not quite as simple as COM's rule "always incref when copying
a pointer, always decref before overwriting or forgetting a
pointer" -- Python's subtler approach has a tiny performance
advantage at the cost of some mental overhead) so eventually
one learns the basic cases, though... hmmm, maybe you ARE
right about the subtlety, all in all, even though it's not quite as
bad as all that -- but having gone to the trouble of coding this
example I think I'll post it anyway!-)


Alex






More information about the Python-list mailing list