Skip to content

PyBCCpython

Katy Huff edited this page Jan 29, 2012 · 5 revisions

Other Wiki Pages: PyBc, Session01, Session02, Session03, Session04, Session05, Session06, Session07, Session08, Session09, f2py, swig, Cpython, Cython, PyTables, PyTaps, PythonBots, Django, GIS, AdvancedPython, WxPython, standardlib,

Using the C/Python Interface

The standard implementation of Python, CPython (that's what you're all using), contains a C API allowing programmers to communicate between C and Python. This allows us to either ''extend'' Python with new functionality, or ''embed'' Python into C applications. In this case, we'll be talking about ''extending''.

Why would you want to write Python in C? There are two main reasons: first, you may want to add a feature to Python that you can't implement from inside Python, but there's a C library that allows it. Second, you may have something written in Python that's simply not fast enough. In this case, other options, like Cython, exist, but experienced C programmers may be more comfortable using the Python C API.

:: Aside: Reference Counting

C doesn't support automatic reference counting. This means we're forced to manually manage the reference counts of objects in Python. To do this, we use the functions Py_INCREF and Py_DECREF. You should also be aware of which functions ''change'' the reference count (most of them) and which ones "steal" a reference. The Python C API [http://docs.python.org/c-api/index.html documentation] will tell you all the functions that steal references.

The Anatomy of an Extension Module

#!CodeExample
#!c
#include <Python.h>
#include <stdio.h>

static PyObject * hello_world(PyObject *self, PyObject *args) {

printf("Hello, world!n"); Py_RETURN_NONE;

}

static PyMethodDef methods[] = {
{"hello_world", hello_world, METH_VARARGS, "Greet the world!"}, {NULL, NULL, 0, NULL}

};

PyMODINIT_FUNC inithello(void) {

PyObject *m; m = Py_InitModule("hello", methods); if (m == NULL) return;

}

The first step is #including Python.h, which pulls in all the necessary definitions to use the Python C API. We can then start defining functions in our extension module. There are several types of functions, but the most notable are function with variable number of arguments (:: METH_VARARGS), or functions with keyword arguments ( METH_KEYWORDS). The C signature for all varargs functions is:

#!CodeExample
#!c

static PyObject * hello_world(PyObject *self, PyObject *args)

In the case of methods, self refers to the instance on which we are calling the method, and in the case of free/class functions, self refers to the class or module. Note that our function is declared static. While not strictly necessary, this helps to avoid name collisions when Python loads your extension module. The only non-static function in your C code should be your init function, which we'll discuss below.

You'll also notice that we end our function with Py_RETURN_NONE;. You should ''always'' return something from your C functions, even if it's None. Returning NULL or 0 signifies that an error occurred.

Later, we create an array of PyMethodDef objects, which are what actually link our C functions to Python. For our hello_world function, we write:

#!CodeExample
#!c

{"hello_world", hello_world, METH_VARARGS, "Greet the world!"}

This gives, in order, the Python name of the function, the C function to use, the function's argument type, and the function's docstring. Once we've defined all the functions we want, we end the array with a PyMethodDef full of NULLs as a sentinel.

Finally, we create a function to initialize our module. The function must be named "init<modulename>" so that Python can find it. Inside the function, we call Py_InitModule("hello", methods), which initializes a Python module named hello with the methods listed inside methods. We're done!

Building an Extension Module

But wait... how do we actually compile our extension module? Are we going to have to figure out a bunch of obscure compiler commands that vary across platforms? No! Python extension modules, like other Python modules, can be built with distutils. Below, we see a simple setup.py file that will compile our extension module:

#!CodeExample
#!python

from distutils.core import setup, Extension

hello = Extension('hello',
sources = ['hello.c'] )
setup(name = 'Hello',
version = '1.0', description = 'C/Python example', author = 'Python Bootcamp', author_email = 'pybc@address.invalid', ext_modules = [hello], )

Arguments

Our function above isn't very useful. It doesn't take any arguments and it doesn't return anything either. Let's make a (slightly) more useful function that accepts some arguments:

#!CodeExample
#!c

static PyObject * snowclone(PyObject *self, PyObject *args) {

int num_snow; int num_other; char *people; char *other;

if( PyArg=ParseTuple(args, "isis", &num=snow, &people, &num=other, &other) == 0)
return NULL;
printf("If Eskimos have %d words for snow, %s surely have %d words for "
"%s.n", num_snow, people, num_other, other);

Py_RETURN_NONE;

}

The key here is PyArg_ParseTuple. When you call a C function from Python, it builds a tuple and passes it to C. We can then extract the values in this tuple using a format string, in our case "isis". Each character represents one argument of a particular type; "i" is an int, and "s" is a string. There are a wide variety of types that you can specify. Some other important ones are "d" for a double (Python float) and "O" for any Python object.

After specifying the format string, we pass in the addresses of our C variables, one per argument. Python will convert its Python objects to the appropriate C data type (if possible) and fill in the values. If Python can't parse the tuple, :: PyArg_ParseTuple will return 0, and we can return with an error.

Keyword Arguments

Expanding upon this, we can create a C function that accepts keyword arguments. This is especially useful when you have many arguments, or multiple optional arguments.

#!CodeExample
#!c

static PyObject * madlib(PyObject *self, PyObject *args, PyObject *kw) {

static char *kwlist[] = {"name_of_person", "place", "past_tense_verb",
"noun", 0};

char *name; char *place; char *verb; char *noun;

if( PyArg_ParseTupleAndKeywords(args, kw, "ssss", kwlist, &name, &place,
&verb, &noun) == 0) return NULL;

printf("One day, %s went to %s and %s your %s.n", name, place, verb, noun); Py_RETURN_NONE;

}

The key difference is that we now use :: PyArg_ParseTupleAndKeywords and pass in an array of strings called kwlist. As you'd expect, kwlist is a list of names for our keyword arguments.

Return Values

Accepting values to our C functions is nice, but what about returning them? Well, since all our functions are defined as returning a PyObject * (the generic "Python object" type), we just need to create (or find) a Python object to return. Most basic types have a simple function for creating a Python object from a C variable, like so:

#!CodeExample
#!c

static PyObject * the_answer(PyObject *self, PyObject *args) {

return PyInt_FromLong(42);

}

For more complicated values (or if you merely prefer the syntax), we can use Py_BuildValue instead:

#!CodeExample
#!c

static PyObject * modf(PyObject *self, PyObject *args) {

double x, f, i; PyArg_ParseTuple(args, "d", &x); f = modf(x, &i); return Py_BuildValue("(dd)", i, f);

}

Above and Beyond

Obviously, there are many things we haven't covered, such as creating classes. The Python C API allows access to every feature available in Python, though it's oftentimes quite verbose. To read more about how to use the Python C API, consult the official [http://docs.python.org/extending/ documentation].

Clone this wiki locally