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 programming API that Python provides but there are many different to deal with C.
Code #1 : [work.c
] C-Code that we are dealing.
#include <math.h> int gcd( int x, int y)
{ int g = y;
while (x > 0 )
{
g = x;
x = y % x;
y = g;
}
return g;
} int divide( int a, int b, int * remainder)
{ int quot = a / b;
* remainder = a % b;
return quot;
} double avg(double * a, int n)
{ int i;
double total = 0.0 ;
for (i = 0 ; i < n; i + + )
{
total + = a[i];
}
return total / n;
} typedef struct Point { double x, y;
} Point; double distance(Point * p1, Point * p2)
{ return hypot(p1 - >x - p2 - >x, p1 - >y - p2 - >y);
} |
Above code has different C-programming features.
gcd()
divide() – returning multiple values, one through a pointer argument
avg() – performing a data reduction across a C array
Point and distance() – involve C structures.
Let’s assume that the code above is found in a file named work.c and it has been compiled into a library libsample that can be linked to other C code. Now, we have a number of C functions that have been compiled into a shared library. So, we call the functions entirely from Python without having to write additional C code or using a third-party extension tool.
Using ctypes :
Python ctypes will come to play but make sure the C code, that is to be converted, has been compiled into a shared library that is compatible with the Python interpreter (e.g., same architecture, word size, compiler, etc.).
Further the libsample.so file has been placed in the same directory as the work.py
. Let’s understand work.py
now.
Code #2 : Python module that wraps around resulting library to access it
# work.py import ctypes
import os
# locating the 'libsample.so' file in the # same directory as this file _file = 'libsample.so'
_path = os.path.join( * (os.path.split(__file__)[: - 1 ] + (_file, )))
_mod = ctypes.cdll.LoadLibrary(_path)
|
Code #3 : Accessing code
# int gcd(int, int) gcd = _mod.gcd
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
gcd.restype = ctypes.c_int
# int divide(int, int, int *) _divide = _mod.divide
_divide.argtypes = (ctypes.c_int, ctypes.c_int,
ctypes.POINTER(ctypes.c_int))
_divide.restype = ctypes.c_int
def divide(x, y):
rem = ctypes.c_int()
quot = _divide(x, y, rem)
return quot, rem.value
# void avg(double *, int n) # Define a special type for the 'double *' argument class DoubleArrayType:
def from_param( self , param):
typename = type (param).__name__
if hasattr ( self , 'from_' + typename):
return getattr ( self , 'from_' + typename)(param)
elif isinstance (param, ctypes.Array):
return param
else :
raise TypeError( "Can't convert % s" % typename)
# Cast from array.array objects
def from_array( self , param):
if param.typecode ! = 'd' :
raise TypeError( 'must be an array of doubles' )
ptr, _ = param.buffer_info()
return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
# Cast from lists / tuples
def from_list( self , param):
val = ((ctypes.c_double) * len (param))( * param)
return val
from_tuple = from_list
# Cast from a numpy array
def from_ndarray( self , param):
return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
DoubleArray = DoubleArrayType()
_avg = _mod.avg
_avg.argtypes = (DoubleArray, ctypes.c_int)
_avg.restype = ctypes.c_double
def avg(values):
return _avg(values, len (values))
# struct Point { } class Point(ctypes.Structure):
_fields_ = [( 'x' , ctypes.c_double), ( 'y' , ctypes.c_double)]
# double distance(Point *, Point *) distance = _mod.distance
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
distance.restype = ctypes.c_double
|
Now, one can easily load the module and use the resulting C functions. See the next part – Using C codes in Python | Set 2.