This time we'll try a slightly more complicated C program.
One, c program header file:
#ifndef __sample_h__#define __sample_h__#include <math.h> #ifdef __cplusplusextern "C" {#endifint gcd (int x, int y) ; int In_mandel (double x0, double y0, int n), int divide (int a, int b, int *remainder);d ouble avg (double *a, int n); /* A C data structure */typedef struct Point { double x, y;} Point; Double distance (point *p1, point *p2), #ifdef __cplusplus} #endif #endif
SOURCE program:
divide()A function is an example of a C function that returns multiple values, one of which is the way a pointer parameter is passed.
avg()The function performs a data aggregation operation through a C array.
point and distance () function involves the C struct body.
/* sample.c * * #include "sample.h"/* Compute the greatest common divisor */int gcd ( int x, int y) {int g = y; while (x > 0) {g = x; x = y% x; y = g; } return G;} /* Test if (x0,y0) is in the Mandelbrot set or not */int In_mandel (double x0, double y0, int n) {double x=0,y=0,xtemp; while (n > 0) {xtemp = x*x-y*y + x0; y = 2*x*y + y0; x = xtemp; n-= 1; if (x*x + y*y > 4) return 0; } return 1;} /* Divide numbers */int Divide (int A, int b, int *remainder) {int quot = A/b; *remainder = a% B; return quot;} /* Average values in an array */double avg (double *a, int n) {int i; Double total = 0.0; for (i = 0; i < n; i++) {total + = A[i]; } return total/n;} /* Function involving a C data structure */double distance (point *p1, point *p2) {return Hypot (P1->x-p2->x, p1 ->y-p2->y);}
After the so file is generated, we try to call these methods.
Second, Python encapsulation
.argtypesA property is a tuple that contains the input of a function on time, and .restype is the corresponding return type.
ctypesA large number of type objects (such as c_double, C_int, C_short, c_float, etc.) are defined, representing the corresponding C data types.
Binding of these types of signatures is an important step if you want Python to be able to pass the correct parameter types and transform the data correctly. If you do not do this, not only does the code not work properly, it may also cause the entire interpreter process to hang.
Import C library File
Import Osimport ctypes_mod = Ctypes.cdll.LoadLibrary ('./libsample.so ')
Simple Data type function encapsulation
In fact, because of the parameter type of this function C language and the Python language type is one by one corresponding, so even if the.argtypes与.restype注释掉也可以正常运行,但建议进行转换
gcd
Original function
/* Compute The greatest common divisor */int gcd (int x, int y) { int g = y; while (x > 0) { g = x; x = y% x; y = g; } return g;}
Call
# int gcd (int, int) gcd = _mod.gcdgcd.argtypes = (ctypes.c_int, ctypes.c_int) Gcd.restype = Ctypes.c_intprint (gcd (35, 42)) c7/># 7
In_mandel:
Original function
/* Test if (x0,y0) is in the Mandelbrot set or not */int In_mandel (double x0, double y0, int n) { double x=0,y=0,xtemp ; while (n > 0) { xtemp = x*x-y*y + x0; y = 2*x*y + y0; x = xtemp; n-= 1; if (x*x + y*y > 4) return 0; } return 1;}
Call
# int In_mandel (double, double, int) In_mandel = _mod.in_mandelin_mandel.argtypes = (ctypes.c_double, ctypes.c_double, Ctypes.c_int) In_mandel.restype = Ctypes.c_intprint (In_mandel (0,0,500)) # 1
Contains the pointer parameter function--the pointer is used to modify the variable
divide()The function returns a result value by dividing one parameter by another, but the pointer is an operation that is not supported in Python.
/* Divide numbers */int Divide (int A, int b, int *remainder) { int quot = A/b; *remainder = a% B; return quot;}
For parameters that involve pointers, you usually need to build a corresponding cTYPES object and pass it in as follows:
divide = _mod.dividex = Ctypes.c_int () Divide.argtypes = (Ctypes.c_int, Ctypes.c_int, cTYPES. POINTER (Ctypes.c_int)) print (Divide (10,3,x)) print (X.value)
Here, an ctypes.c_int instance is created and passed in as a pointer. Unlike normal python shaping, an c_int object can be modified. .valueproperty can be used to get or change this value.
More generally, for a function with pointers, we will add a layer of encapsulation after the call, so that the variables modified by the pointer by return, so go to C style, make the code more like Python style:
# 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_intdef divide (x, y): rem = Ctypes.c_int () quot = _divide (x, Y, rem) return quot, rem.value
Contains the pointer parameter function--the pointer is used to receive the array
avg()function is also a new challenge. C code expects to receive a pointer and an array of length values. However, in Python, we have to consider this question: What is an array? Is it a list? A tuple? or array an array in the module? Or an numpy array? Or is that all? In fact, a Python "array" has many forms, and you might want to support multiple possibilities.
/* Average values in an array */double avg (double *a, int n) { int i; Double total = 0.0; for (i = 0; i < n; i++) {total + = A[i]; } return total/n;}
Python-c arrays
(Ctypes.c_int * Array length) (array Element)
The intrinsic logic is: for lists and tuples, from_list methods convert them to an ctypes array object
Nums = [1, 2, 3]a = (Ctypes.c_int * len (nums)) (3,4,5) print (a) print (a[0], a[1], a[2]) # <__main__.c_int_array_3 object at 0x7f2767d4fd08># 3 4 5
The array object itself is stored in the same structure as the C array, and the direct fetch memory address is passed to the C pointer:
Import Arraya = Array.array (' d ', [I/C]) print (a) ptr = A.buffer_info () # Returns a tuple: (address, length) print (ptr[0]) print ( Ctypes.cast (Ptr[0], ctypes. POINTER (ctypes.c_double)) # Destination Address deposit pointer
The NumPy array comes with ctypes.data_as (ctypes pointer) method, which is more convenient.
As a basis, we can make a class that converts a python sequence into a C array pointer (which I don't fully understand) and encapsulates the AVG function:
# void Avg (double *, int n) # Define a special type for the ' double * ' Argumentclass doublearraytype:def from_param (sel F, param): typename = Type (param). __name__ if hasattr (self, ' from_ ' + typename): Return GetAttr (S Elf, ' From_ ' + typename) (param) elif isinstance (param, ctypes. Array): return param else:raise TypeError ("Can ' t convert%s"% typename) # Cast from Arra Y.array Objects def from_array (self, param): if param.typecode! = ' d ': Raise TypeError (' must be ' AR Ray 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 (p Aram)) (*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_doubledef avg (values): Return _avg (values, Len ( Values))Structural body
/* A C data structure */typedef struct Point { double x, y;} Point; /* Function involving a C data structure */double distance (point *p1, point *p2) { return Hypot (P1->x-p2->x, p 1->y-p2->y);}
Inherit cTYPES. STRUCTURE,_FIELDS_ naming structure Body elements:
# struct Point {}class point (ctypes. Structure): _fields_ = [(' x ', ctypes.c_double), (' y ', ctypes.c_double)]# double distance (point *, point *) Distance = _mod.distancedistance.argtypes = (ctypes. POINTER (point), cTYPES. POINTER (point)) Distance.restype = ctypes.c_double
>>>P1=Point (1,2) > >> p2 = point (4,5) > >> p1. X1.0>>> p1. Y2.0>>> distance< Span class= "P" > (p1,p2) 4.242640687119285>>>
"Python Coolbook" uses ctypes to access C code _