Welcome, guest | Sign In | My Account | Store | Cart

This recipe shows how to define a new Python class from a C extension module. The class methods are implemented in C, but the class can still be instantiated, extended, subclassed, etc. from Python. The same technique can also be used to extend an existing Python class with methods written in C.

Python, 54 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <Python.h>

static PyObject* Foo_init(PyObject *self, PyObject *args)
{
    printf("Foo.__init__ called\n");
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject* Foo_doSomething(PyObject *self, PyObject *args)
{
    printf("Foo.doSomething called\n");
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef FooMethods[] = 
{
    {"__init__", Foo_init, METH_VARARGS, 
	 "doc string"},
    {"doSomething", Foo_doSomething, METH_VARARGS,
	 "doc string"},
    {NULL},
};

static PyMethodDef ModuleMethods[] = { {NULL} };

#ifdef __cplusplus
extern "C"
#endif
void initFoo()
{
    PyMethodDef *def;

    /* create a new module and class */
    PyObject *module = Py_InitModule("Foo", ModuleMethods);
    PyObject *moduleDict = PyModule_GetDict(module);
    PyObject *classDict = PyDict_New();
    PyObject *className = PyString_FromString("Foo");
    PyObject *fooClass = PyClass_New(NULL, classDict, className);
    PyDict_SetItemString(moduleDict, "Foo", fooClass);
    Py_DECREF(classDict);
    Py_DECREF(className);
    Py_DECREF(fooClass);
    
    /* add methods to class */
    for (def = FooMethods; def->ml_name != NULL; def++) {
	PyObject *func = PyCFunction_New(def, NULL);
	PyObject *method = PyMethod_New(func, NULL, fooClass);
	PyDict_SetItemString(classDict, def->ml_name, method);
	Py_DECREF(func);
	Py_DECREF(method);
    }
}

The usual method of creating new types in an extension module is to define a new instance of PyTypeObject and provide callbacks to the various C functions that implement the type. However, it may be better to define the new type as a Python class so that the type can be instantiated, subclassed, etc. from Python. In some cases, defining a custom exception type for instance, it is even required that the new type be a Python class.

The class methods are defined in C functions and described by a table of PyMethodDef's in the same way that module methods are defined. The key that allows these methods to become class methods is that they are each wrapped first in a PyCFunction object and then in a PyMethod object. The PyCFunction turns the C function into a python object, and the PyMethod associates the function with a particular class. Finally, the methods are added to the class' dictionary which makes them callable by instances of the class.

Notes: 1) Base classes can be specified for the new class by passing a tuple of class objects as the first arg to PyClass_New. These can be existing python classes. 2) The second argument passed to PyCFunction_New becomes the "self" arg passed to the C function. This can be any Python object, but it's not very useful in this context since you could just as easily keep a static C variable. 3) The class instance is passed to the C functions as the first argument in the "args" tuple.

2 comments

Vladimir Sironja 22 years, 4 months ago  # | flag

getattr trouble.

Hi,
great article. Here a short follow-up.
Problems arise when trying to implement __getattr__ in a way described above.
The C-implementation is not called if added in a same way as "doSomething".
According to Python Language Reference, __getattr__ must be available at the
time of class definition and will not be called if added to __dict__ later.
So, __getattr__, __setattr__ and co must be added before calling PyClass_New.
Obviously, they are added as functions, and not as methods.

Code:

static PyMethodDef AttrMethods[] =
{
  { "__setattr__", test_setattr, METH_VARARGS },
  { "__getattr__", test_getattr, METH_VARARGS },
  { NULL, NULL },
};

for(PyMethodDef* _def = AttrMethods; _def->ml_name; _def++) {
      PyObject *func = PyCFunction_New(_def, NULL);
      PyDict_SetItemString(classDict, _def->ml_name, func);
      Py_DECREF(func);
  }
  // finally add class and all the methods as before
  testClass = PyClass_New(NULL, classDict, className);
  // ...

  // skipped

static PyObject* test_getattr(PyObject *self, PyObject *args) {
    PyObject *selfobj, *arg0;
    if(!PyArg_ParseTuple(args, "OO:test_foo", &selfobj, &arg0)) {
        // handle error
    }
    if (!PyString_Check(arg0)) {
        return NULL;
    }
    char* name = PyString_AsString(arg0);
    if (!name) return NULL;
    if (strcmp(name, "id")==0) {
        return Py_BuildValue("i", 42);
    }
    // throw attribute not found and and ...
}

Bye
Vlado
myers 20 years, 8 months ago  # | flag

With python 2.2 or greater you can do a lot better. With python 2.2 or better you can do a lot better than this. Python 2.2 and better have the new Class/Type unification. This was not documented until version 2.3. See the Python 2.3 "Extending and Embedding" for an example of the new interface.

http://python.org/doc/2.3c1/ext/ext.html

Created by Brent Burley on Wed, 25 Apr 2001 (PSF)
Python recipes (4591)
Brent Burley's recipes (7)

Required Modules

  • (none specified)

Other Information and Tasks