Cython to Wrap Existing C Code - GeeksforGeeks

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 :
# 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 :
# work.pyx# Import the low-level C declarations        cimport cwork# Importing functionalities from Python# and the C stdlibfrom cpython.pycapsule cimport * from libc.stdlib cimport malloc, free  # Wrappersdef 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 :
# Destructor for cleaning up Point objectscdef del_Point(object obj):    pt = <csample.Point *> PyCapsule_GetPointer(obj, "Point")    free(<void *> pt)      # Create a Point object and return as a capsuledef 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:
# importing librariesfrom distutils.core import setupfrom distutils.extension import Extensionfrom 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 % python3 setup.py build_ext --inplacerunning build_ext  cythoning work.pyx to work.cbuilding '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.sobash %
Now, we have an extension module work.so. Let’s see how it works.
Code #6 :
import sampleprint ("GCD : ", sample.gcd(12, 8))  print ("\nDivision : ", sample.divide(42,10))  import arrayarr = 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.
Marketing
Python-ctype
Python
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
How to Install PIP on Windows ?
How To Convert Python Dictionary To JSON?
Check if element exists in list in Python
How to drop one or multiple columns in Pandas Dataframe
Python Classes and Objects
Python | os.path.join() method
Create a directory in Python
Python | Get unique values from a list
Python | Pandas dataframe.groupby()
Defaultdict in Python