Skip to content

PyBCCpython

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

TOC(PyBc, PyBc/Session01, PyBc/Session02, PyBc/Session03, PyBc/Session04, PyBc/Session05, PyBc/Session06, PyBc/Session07, PyBc/Session08, PyBc/Session09, PyBc/f2py, PyBc/swig, PyBc/Cpython, PyBc/Cython, PyBc/PyTables, PyBc/PyTaps, PyBc/PythonBots, PyBc/Django, PyBc/GIS, PyBc/AdvancedPython, PyBc/WxPython, PyBc/standardlib, depth=1)

= 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.

{{{ #!div style="border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto;" '''Aside: Reference Counting''[[BR]]

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 {{{NULL}}}s 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 = '[email protected]', 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