Cython to Wrap Existing C Code
Last Updated :
29 Mar, 2019
What is Cython ?
It is an optimizing static compiler for both the Python programming language and the extended Cython programming language. It is used to make it easy to write C extensions for Python as easy as Python itself.
It comes up with many
helpful features :
- Writing a Python code that calls back and forth from and to C/C++ code.
- Easily tuning of readable Python code into plain C performance by adding static type declarations.
- Use of combined source code level debugging to find bugs in given Python, Cython and C code.
- Efficient interaction with large data sets, e.g. using multi-dimensional NumPy arrays.
- Integration with existing code and data from low-level or high-performance libraries and applications.
To make an extension with Cython is a tricky task to perform. Doing so, one needs to create a collection of wrapper functions. Assuming that the work code shown has been compiled into a C library called
libwork. The code below will create a file named
csample.pxd
.
Code #1 :
Python3 1==
# cwork.pxd
#
# Declarations of "external" C
# functions and structures
cdef extern from "work.h":
int gcd(int, int)
int divide(int, int, int *)
double avg(double *, int) nogil
ctypedef struct Point:
double x
double y
double distance(Point *, Point *)
In Cython, the code above will work as a C header file. The initial declaration
cdef extern from
"work.h"
declares the required C header file. Declarations that follow are taken from the header. The name of this file is
cwork.pxd
. Next target is to create a
work.pyx
file which will define wrappers that bridge the Python interpreter to the underlying C code declared in the
cwork.pxd
file.
Code #2 :
Python3 1==
# work.pyx
# Import the low-level C declarations
cimport cwork
# Importing functionalities from Python
# and the C stdlib
from cpython.pycapsule cimport *
from libc.stdlib cimport malloc, free
# Wrappers
def gcd(unsigned int x, unsigned int y):
return cwork.gcd(x, y)
def divide(x, y):
cdef int rem
quot = cwork.divide(x, y, &rem)
return quot, rem
def avg(double[:] a):
cdef:
int sz
double result
sz = a.size
with nogil:
result = cwork.avg(<double *> &a[0], sz)
return result
Code #3 :
Python3 1==
# Destructor for cleaning up Point objects
cdef del_Point(object obj):
pt = <csample.Point *> PyCapsule_GetPointer(obj, "Point")
free(<void *> pt)
# Create a Point object and return as a capsule
def Point(double x, double y):
cdef csample.Point * p
p = <csample.Point *> malloc(sizeof(csample.Point))
if p == NULL:
raise MemoryError("No memory to make a Point")
p.x = x
p.y = y
return PyCapsule_New(<void *>p, "Point",
<PyCapsule_Destructor>del_Point)
def distance(p1, p2):
pt1 = <csample.Point *> PyCapsule_GetPointer(p1, "Point")
pt2 = <csample.Point *> PyCapsule_GetPointer(p2, "Point")
return csample.distance(pt1, pt2)
Finally, to build the extension module, create a
work.py
file.
Code #4:
Python3
# importing libraries
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension('work',
['work.pyx'],
libraries=['work'],
library_dirs=['.'])]
setup(name = 'work extension module',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules)
Code #5 : Building resulting module for experimentation.
bash
bash % python3 setup.py build_ext --inplace
running build_ext
cythoning work.pyx to work.c
building 'work' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/usr/local/include/python3.3m -c work.c
-o build/temp.macosx-10.6-x86_64-3.3/work.o
gcc -bundle -undefined dynamic_lookup build/temp.macosx-10.6-x86_64-3.3/work.o
-L. -lwork -o work.so
bash %
Now, we have an extension module
work.so
. Let's see how it works.
Code #6 :
Python3
import sample
print ("GCD : ", sample.gcd(12, 8))
print ("\nDivision : ", sample.divide(42,10))
import array
arr = array.array('d',[1,2,3])
print ("\nAverage : ", sample.avg(a)
pt1 = sample.Point(2,3)
pt2 = sample.Point(4,5)
print ("\npt1 : ", pt1)
print ("\npt2 : ", pt2)
print ("\nDistance between the two points : ",
sample.distance(pt1, pt2))
Output :
GCD : 4
Division : (4, 2)
Average : 2.0
pt1 : <capsule object "Point" at 0x1005d1e70>
pt2 : <capsule object "Point" at 0x1005d1ea0>
Distance between the two points : 2.8284271247461903
At a high level, using Cython is modeled after C. The
.pxd files merely contain C definitions (similar to
.h
files) and the
.pyx
files contain implementation (similar to a
.c
file). The
cimport statement is used by Cython to import definitions from a
.pxd
file. This is different than using a normal Python import statement, which would load a regular Python module.
Similar Reads
Swig to wrap C Code Prerequisite: Using C codes in Python, Wrapping C/C++ for Python using SWIG Suppose we have given a C code and it needs to be accessed as a C extension module. So, for the given task - Swig Wrapper Generator is used. Swig operates by parsing C header files and automatically creating extension code.
3 min read
Wrapping C/C++ for Python using SWIG - Set 1 There is no doubt that C is faster than Python then how do Python library like Numpy perform huge number crunching job so fast and efficiently? Actually, libraries like Numpy are not completely written in Python instead, some parts of the library are written in C which provides performance boost. Af
5 min read
Using C codes in Python | Set 1 Prerequisite: How to Call a C function in Python Let's discuss the problem of accessing C code from Python. As it is very evident that many of Pythonâs built-in libraries are written in C. So, to access C is a very important part of making Python talk to existing libraries. There is an extensive C p
4 min read
Using C codes in Python | Set 2 Prerequisite: Using C codes in Python | Set 1 In the previous article, we have discussed how to access C code in Python. Now, let's see how to access C functions. Code #1 : Accessing C functions with Python Python3 1== import work print ("GCD : ", work.gcd(35, 42)) print ("\ndivide :
2 min read
C Extension Module using Python Writing a simple C extension module directly using Pythonâs extension API and no other tools. It is straightforward to make a handcrafted extension module for a simple C code. But first, we have to make sure that the C code has a proper header file. Code #1 : C #include <math.h> extern int gcd
4 min read
Python | C Strings of Doubtful Encoding | Set-1 One can convert strings between C and Python vice-versa but the C encoding is of a doubtful or unknown nature. Let's suppose that a given C data is supposed to be UTF-8, but itâs not being strictly enforced. So, it is important to handle such kind of malformed data so that it doesnât crash Python or
2 min read