|
| 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(<ime); |
| 144 | + return ctime(<ime); |
| 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