Python/C API: Python class instance information in C?

Alex Martelli aleax at aleax.it
Fri Mar 21 04:02:24 EST 2003


<posted & mailed>

Bernt Ribbum wrote:

> In a C module function, is it possible to extract information about a
> Python class instance being sent as argument?

Yes.  You can basically do in your C code all that you might do from
Python code, albeit more clumsily and less productively of course. And
this in particular applies to introspection, as well as other tasks.


> To explain a bit further... A C module (METH_VARARGS wrapper) function
> prototype looks something like
> 
> PyObject *spam_func(PyObject *self, PyObject *args);
> 
> The Python arguments are found (possibly inside a tuple) in "args", and I
> can handle this for standard Python types like ints, floats, etc., and
> also for types exported from my C module. What I stumble on, is Python
> code like this:
> 
> import spam
> class X:
>   pass
> x = X()
> spam.func(x)
> 
> In this case I don't know how to extract information about the object. All
> I can find (from the C debugger) is that the object in question has an
> ob_type of _PyInstance_Type. (A call to PyInstance_Check() will return
> true, but unfortunately the documentation states "There are very few
> functions specific to instance objects". In fact, there is only the check
> function above and two New functions. Nothing else.)
> 
> Is it possible for me to get more information here? I would like to know,
> at least, that this object is of type "class X" or similar.

By PyInstance_Check you identify instances of CLASSIC classes only, of
course; i.e. if in your example above X inherited from object (and thus
was a newstyle class) its instances wouldn't pass PyInstance_Check.  If
you also want to handle instances of newstyle classes you'll have to
work those differently.  But let's focus for now on classic class isntances.

However, the general idea is that you work with the abstract protocol,
i.e. the PyObject_... functions, getting the relevant attributes.  Here
is one silly example (not thoroughly tested) -- save as intro.py:

#include <Python.h>
#include <stdio.h>

static PyObject*
intro(PyObject* self, PyObject* args)
{
    PyObject *theobj, *theclas, *thecnam;
    if(!PyArg_ParseTuple(args, "O", &theobj))
        return 0;
    if(PyInstance_Check(theobj)) {
        printf("Is an instance.\n");
        theclas = PyObject_GetAttrString(theobj, "__class__");
        if(!theclas)
            return 0;
        thecnam = PyObject_GetAttrString(theclas, "__name__");
        if(!thecnam) {
            Py_DECREF(theclas);
            return 0;
        }
        if(!PyString_Check(thecnam)) {
            Py_DECREF(theclas);
            Py_DECREF(thecnam);
            return PyErr_Format(PyExc_RuntimeError,
                "class name is not a string (?)");
        }
        printf("of class %s\n", PyString_AS_STRING(thecnam));
        Py_DECREF(theclas);
        Py_DECREF(thecnam);
    } else {
        printf("Not an instance.\n");
    }
    return Py_BuildValue("");
}

static PyMethodDef introMethods[] = {
    {"intro", intro, METH_VARARGS, "introspection example"},
    {0}
};

void
initintro(void)
{
    Py_InitModule("intro", introMethods);
}

and build/install with the usual setup.py:

from distutils.core import setup, Extension

setup(name = "intro",
    version = "1.0",
    description = "introspection from C",
    maintainer = "Alex Martelli",
    maintainer_email = "aleaxit at yahoo.com",

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


Now for example:

>>> import intro
>>> intro.intro(23)
Not an instance.
>>> class X: pass
...
>>> intro.intro(X())
Is an instance.
of class X
>>>



Alex





More information about the Python-list mailing list