Skip to content

Commit 4b4063c

Browse files
committed
added chapter on python C extensions
1 parent 626ff20 commit 4b4063c

1 file changed

Lines changed: 384 additions & 0 deletions

File tree

python_c_extension.rst

Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
1+
Python C extensions
2+
===================
3+
4+
An interesting feature offered to developers by the CPython
5+
implementation is the ease of interfacing C code to Python.
6+
7+
There are three key methods developers use to call C functions from
8+
their python code - ``ctypes``, ``SWIG`` and ``Python/C API``. Each
9+
method comes with it's own merits and demerits.
10+
11+
Firstly, why would you want to interface C with Python?
12+
13+
A few common reasons are :
14+
15+
- You want speed and you know C is about 50x faster than Python.
16+
- Certain legacy C libraries work just as well as you want them to, so you don't want to rewrite them in python.
17+
- Certain low level resource access - from memory to file interfaces.
18+
- Just because you want to.
19+
20+
1. CTypes
21+
---------
22+
23+
The Python `ctypes
24+
module <https://docs.python.org/2/library/ctypes.html>`__ is probably
25+
the most easiest way to call C functions from Python. The ctypes module
26+
provides C compatible data types and functions to load DLLs so that
27+
calls can be made to C shared libraries without having to modify them.
28+
The fact that the C side needn't be touched adds to the simplicity of
29+
this method.
30+
31+
**Example**
32+
33+
Simple C code to add two numbers, save it as ``add.c``
34+
35+
.. code:: c
36+
37+
//sample C file to add 2 numbers - int and floats
38+
39+
#include <stdio.h>
40+
41+
int add_int(int, int);
42+
float add_float(float, float);
43+
44+
int add_int(int num1, int num2){
45+
return num1 + num2;
46+
}
47+
48+
float add_float(float num1, float num2){
49+
return num1 + num2;
50+
}
51+
52+
Next compile the C file to a ``.so`` file (DLL in windows) This will
53+
generate a adder.so file.
54+
55+
.. code:: bash
56+
57+
#For Linux
58+
$ gcc -shared -Wl,-soname,adder -o adder.so -fPIC add.c
59+
60+
#For Mac
61+
$ gcc -shared -Wl,-install_name,adder.so -o adder.so -fPIC add.c
62+
63+
Now in your python code -
64+
65+
.. code:: python
66+
67+
from ctypes import *
68+
69+
#load the shared object file
70+
adder = CDLL('./adder.so')
71+
72+
#Find sum of integers
73+
res_int = adder.add_int(4,5)
74+
print "Sum of 4 and 5 = " + str(res_int)
75+
76+
#Find sum of floats
77+
a = c_float(5.5)
78+
b = c_float(4.1)
79+
80+
add_float = adder.add_float
81+
add_float.restype = c_float
82+
print "Sum of 5.5 and 4.1 = ", str(add_float(a, b))
83+
84+
And the output is as follows
85+
86+
::
87+
88+
Sum of 4 and 5 = 9
89+
Sum of 5.5 and 4.1 = 9.60000038147
90+
91+
In this example the C file is self explanatory - it contains two
92+
functions, one to add two integers and another to add two floats.
93+
94+
In the python file, first the ctypes module is imported. Then the CDLL
95+
function of the ctypes module is used to load the shared lib file we
96+
created. The functions defined in the C lib is now available to us via
97+
the ``adder`` variable. When ``adder.add_int()`` is called, internally a
98+
call is made to the ``add_int`` C function. The ctypes interface allows
99+
us to use native python integers and strings by default while calling
100+
the C functions.
101+
102+
For other types such as boolean or float, we have to use the correct
103+
ctypes. This is seen while passing parameters to the
104+
``adder.add_float()``. We first create the required c\_float types from
105+
python decimal values, and then use them as arguments to the C code.
106+
This method is simple and clean, but limited. For example it's not
107+
possible to manipulate objects on the C side.
108+
109+
2. SWIG
110+
-------
111+
112+
Simplified Wrapper and Interface Generator, or SWIG for short is another
113+
way to interface C code to Python. In this method, the developer must
114+
develop an extra interface file which is an input to SWIG (the command
115+
line utility).
116+
117+
Python developers generally don't use this method, because it is in most
118+
cases unnecessarily complex. This is a great method when you have a
119+
C/C++ code base, and you want to interface it to many different
120+
languages.
121+
122+
**Example** (from the `SWIG website <http://www.swig.org/tutorial.html>`__ )
123+
124+
The C code, ``example.c`` that has a variety of functions and variables
125+
126+
.. code:: c
127+
128+
#include <time.h>
129+
double My_variable = 3.0;
130+
131+
int fact(int n) {
132+
if (n <= 1) return 1;
133+
else return n*fact(n-1);
134+
}
135+
136+
int my_mod(int x, int y) {
137+
return (x%y);
138+
}
139+
140+
char *get_time()
141+
{
142+
time_t ltime;
143+
time(&ltime);
144+
return ctime(&ltime);
145+
}
146+
147+
The interface file - this will remain the same irrespective of the
148+
language you want to port your C code to :
149+
150+
::
151+
152+
/* example.i */
153+
%module example
154+
%{
155+
/* Put header files here or function declarations like below */
156+
extern double My_variable;
157+
extern int fact(int n);
158+
extern int my_mod(int x, int y);
159+
extern char *get_time();
160+
%}
161+
162+
extern double My_variable;
163+
extern int fact(int n);
164+
extern int my_mod(int x, int y);
165+
extern char *get_time();
166+
167+
And now to compile it
168+
169+
::
170+
171+
unix % swig -python example.i
172+
unix % gcc -c example.c example_wrap.c \
173+
-I/usr/local/include/python2.1
174+
unix % ld -shared example.o example_wrap.o -o _example.so
175+
176+
Finally, the Python output
177+
178+
.. code:: python
179+
180+
>>> import example
181+
>>> example.fact(5)
182+
120
183+
>>> example.my_mod(7,3)
184+
1
185+
>>> example.get_time()
186+
'Sun Feb 11 23:01:07 1996'
187+
>>>
188+
189+
As we can see, SWIG achieves the same result, but requires a slightly
190+
more involved effort. But it's worth it if you are targeting multiple
191+
languages.
192+
193+
3. Python/C API
194+
---------------
195+
196+
The `C/Python API <https://docs.python.org/2/c-api/>`__ is probably the
197+
most widely used method - not for it's simplicity but for the fact that
198+
you can manipulate python objects in your C code.
199+
200+
This method requires your C code to be specifically written for
201+
interfacing with Python code. All Python objects are represented as a
202+
PyObject struct and the ``Python.h`` header file provides various
203+
functions to manipulate it. For example if the PyObject is also a
204+
PyListType (basically a list), then we can use the ``PyList_Size()``
205+
function on the struct to get the length of the list. This is equivalent
206+
to calling ``len(list)`` in python. Most of the basic
207+
functions/opertions that are there for native Python objects are made
208+
available in C via the ``Python.h`` header.
209+
210+
**Example**
211+
212+
To write a C extension that adds all the elements in a python list. (all elements are numbers)
213+
214+
Let's start with the final interface we'd like to have, here is the
215+
python file that uses the C extension :
216+
217+
.. code:: python
218+
219+
#Though it looks like an ordinary python import, the addList module is implemented in C
220+
import addList
221+
222+
l = [1,2,3,4,5]
223+
print "Sum of List - " + str(l) + " = " + str(addList.add(l))
224+
225+
The above looks like any ordinary python file, which imports and uses
226+
another python module called ``addList``. The only difference is that
227+
the addList module is not written in Python at all, but rather in C.
228+
229+
Next we'll have a look at the C code that get's built into the
230+
``addList`` Python module. This may seem a bit daunting at first, but
231+
once you understand the various components that go into writing the C
232+
file, it's pretty straight forward.
233+
234+
*adder.c*
235+
236+
.. code:: c
237+
238+
//Python.h has all the required function definitions to manipulate the Python objects
239+
#include <Python.h>
240+
241+
//This is the function that is called from your python code
242+
static PyObject* addList_add(PyObject* self, PyObject* args){
243+
244+
PyObject * listObj;
245+
246+
//The input arguments come as a tuple, we parse the args to get the various variables
247+
//In this case it's only one list variable, which will now be referenced by listObj
248+
if (! PyArg_ParseTuple( args, "O", &listObj))
249+
return NULL;
250+
251+
//length of the list
252+
long length = PyList_Size(listObj);
253+
254+
//iterate over all the elements
255+
int i, sum =0;
256+
for(i = 0; i < length; i++){
257+
//get an element out of the list - the element is also a python objects
258+
PyObject* temp = PyList_GetItem(listObj, i);
259+
//we know that object represents an integer - so convert it into C long
260+
long elem = PyInt_AsLong(temp);
261+
sum += elem;
262+
}
263+
264+
//value returned back to python code - another python object
265+
//build value here converts the C long to a python integer
266+
return Py_BuildValue("i", sum);
267+
}
268+
269+
//This is the docstring that corresponds to our 'add' function.
270+
static char addList_docs[] =
271+
"add( ): add all elements of the list\n";
272+
273+
/* This table contains the relavent info mapping -
274+
<function-name in python module>, <actual-function>,
275+
<type-of-args the function expects>, <docstring associated with the function>
276+
*/
277+
static PyMethodDef addList_funcs[] = {
278+
{"add", (PyCFunction)addList_add, METH_VARARGS, addList_docs},
279+
{NULL, NULL, 0, NULL}
280+
};
281+
282+
/*
283+
addList is the module name, and this is the initialization block of the module.
284+
<desired module name>, <the-info-table>, <module's-docstring>
285+
*/
286+
PyMODINIT_FUNC initaddList(void){
287+
Py_InitModule3("addList", addList_funcs,
288+
"Add all ze lists");
289+
}
290+
291+
A step by step explanation - \* The ``<Python.h>`` file consists of all
292+
the required types (to represent Python object types) and function
293+
definitions (to operate on the python objects). \* Next we write the
294+
function which we plan to call from python. Conventionally the function
295+
names are {module-name}\_{function-name}, which in this case is
296+
``addList_add``. More about the function later. \* Then fill in the info
297+
table - which contains all the relevant info of the functions we desire
298+
to have in the module. Every row corresponds to a function, with the
299+
last one being a sentinel value (row of null elements). \* Finally the
300+
module initialization block which is of the signature
301+
``PyMODINIT_FUNC init{module-name}``.
302+
303+
The function ``addList_add`` accepts arguments as a PyObject type struct
304+
(args is also a tuple type - but since everything in python is an
305+
object, we use the generic PyObject notion). The incoming arguments is
306+
parsed (basically split the tuple into individual elements) by
307+
``PyArg_ParseTuple()``. The first parameter is the argument variable to
308+
be parsed. The second argument is a string that tells us how to parse
309+
each element in the args tuple. The character in the Nth position of the
310+
string tells us the type of the Nth element in the args tuple, example -
311+
'i' would mean integer, 's' would mean string and 'O' would mean a
312+
Python object. Next multiple arguments follow, these are where you would
313+
like the ``PyArg_ParseTuple()`` function to store all the elements that
314+
it has parsed. The number of such arguments is equal to the number of
315+
arguments which the module function expects to receive, and positional
316+
integrity is maintained. For example if we expected a string, integer
317+
and a python list in that order, the function signature would be
318+
319+
.. code:: c
320+
321+
int n;
322+
char *s;
323+
PyObject* list;
324+
PyArg_ParseTuple(args, "siO", &n, &s, &list);
325+
326+
In this case we only have to extract a list object, and store it in the
327+
variable ``listObj``. We then use the ``PyList_Size()`` function on our
328+
list object and get the length. This is similar to how you would call
329+
``len(list)`` in python.
330+
331+
Now we loop through the list, get each element using the
332+
``PyList_GetItem(list, index)`` function. This returns a PyObject\*. But
333+
since we know that the Python objects are also ``PyIntType``, we just
334+
use the ``PyInt_AsLong(PyObj *)`` function to get the required value. We
335+
do this for every element and finally get the sum.
336+
337+
The sum is converted to a python object and is returned to the Python
338+
code with the help of ``Py_BuildValue()``. Here the "i" indicates that
339+
the value we want to build is a python integer object.
340+
341+
Now we build the C module. Save the following code as ``setup.py``
342+
343+
.. code:: python
344+
345+
#build the modules
346+
347+
from distutils.core import setup, Extension
348+
349+
setup(name='addList', version='1.0', \
350+
ext_modules=[Extension('addList', ['adder.c'])])
351+
352+
and run
353+
354+
.. code:: sh
355+
356+
python setup.py install
357+
358+
This should now build and install the C file into the python module we
359+
desire.
360+
361+
After all this hard work, we'll now test if the module works -
362+
363+
.. code:: python
364+
365+
#module that talks to the C code
366+
import addList
367+
368+
l = [1,2,3,4,5]
369+
print "Sum of List - " + str(l) + " = " + str(addList.add(l))
370+
371+
And here is the output
372+
373+
::
374+
375+
Sum of List - [1, 2, 3, 4, 5] = 15
376+
377+
So as you can see, we have developed our first successful C Python
378+
extension using the Python.h API. This method does seem complex at
379+
first, but once you get used to it it can prove to be quite useful.
380+
381+
Other ways to interface C code to Python is to use an alternative and
382+
faster build of python - `Cython <http://cython.org/>`__. But Cython is
383+
a slightly different language than the main stream python we see. Hence
384+
that method is not covered here.

0 commit comments

Comments
 (0)