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 :
Python3 1==
In Cython, the code above will work as a C header file. The initial declaration cdef extern from
Python3 1==
Code #3 :
Python3 1==
Finally, to build the extension module, create a
Python3
Code #5 : Building resulting module for experimentation.
bash
Now, we have an extension module
Python3
- 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.
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 *)
"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 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
# 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)
work.py file.
Code #4:
# 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)
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 %
work.so. Let's see how it works.
Code #6 :
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.8284271247461903At 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.