├── README.md ├── with_C_API ├── README.md ├── ptexample.c ├── pysample.c ├── pysample.h ├── sample.c ├── sample.h ├── setup.py └── setup_sample.py ├── with_Cython ├── csample.pxd ├── sample.c ├── sample.cpython-36m-x86_64-linux-gnu.so ├── sample.h ├── sample.pyx └── setup.py └── with_ctypes ├── README.md ├── sample.c ├── sample.h └── sample.py /README.md: -------------------------------------------------------------------------------- 1 | # Extend Python with C 2 | 实际上ctypes、Python C API或者基于API的Cython,逻辑都是:
3 | `接收Python对象`->`转换为C对象`->`调用C函数`->`返回值转换为Python对象`->`返回`
4 | 的流程,较为值得关切的几点是:
5 | 指针传参问题
6 | 数组传递问题
7 | 结构体传递问题
8 | 高级一点情况会考虑如何绕过GIL提升速度
9 | -------------------------------------------------------------------------------- /with_C_API/README.md: -------------------------------------------------------------------------------- 1 | 2 | #### 资料原文 3 | [python3cookbook第十五章_C语言扩展](http://python3-cookbook.readthedocs.io/zh_CN/latest/chapters/p15_c_extensions.html#) 4 | #### 程序介绍 5 | [『Python CoolBook』C扩展库_其二_demo演示](https://www.cnblogs.com/hellcat/p/9083441.html)
6 | [『Python CoolBook』C扩展库_其三_简单数组操作](https://www.cnblogs.com/hellcat/p/9088524.html)
7 | [『Python CoolBook』C扩展库_其四_结构体操作与Capsule](https://www.cnblogs.com/hellcat/p/9088824.html)
8 | [『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API](https://www.cnblogs.com/hellcat/p/9089723.html)
9 | [『Python CoolBook』C扩展库_其六_从C语言中调用Python代码](https://www.cnblogs.com/hellcat/p/9093602.html)
10 | [『Python CoolBook』高效数组操作](https://www.cnblogs.com/hellcat/p/9130241.html)
11 | -------------------------------------------------------------------------------- /with_C_API/ptexample.c: -------------------------------------------------------------------------------- 1 | /* ptexample.c */ 2 | 3 | /* Include the header associated with the other module */ 4 | #include "pysample.h" 5 | 6 | /* An extension function that uses the exported API */ 7 | static PyObject *print_point(PyObject *self, PyObject *args) { 8 | PyObject *obj; 9 | Point *p; 10 | if (!PyArg_ParseTuple(args,"O", &obj)) { 11 | return NULL; 12 | } 13 | 14 | /* Note: This is defined in a different module */ 15 | p = PyPoint_AsPoint(obj); 16 | if (!p) { 17 | return NULL; 18 | } 19 | printf("%f %f\n", p->x, p->y); 20 | return Py_BuildValue(""); 21 | } 22 | 23 | static PyMethodDef PtExampleMethods[] = { 24 | {"print_point", print_point, METH_VARARGS, "output a point"}, 25 | { NULL, NULL, 0, NULL} 26 | }; 27 | 28 | static struct PyModuleDef ptexamplemodule = { 29 | PyModuleDef_HEAD_INIT, 30 | "ptexample", /* name of module */ 31 | "A module that imports an API", /* Doc string (may be NULL) */ 32 | -1, /* Size of per-interpreter state or -1 */ 33 | PtExampleMethods /* Method table */ 34 | }; 35 | 36 | /* Module initialization function */ 37 | PyMODINIT_FUNC 38 | PyInit_ptexample(void) { 39 | PyObject *m; 40 | 41 | m = PyModule_Create(&ptexamplemodule); 42 | if (m == NULL) 43 | return NULL; 44 | 45 | /* Import sample, loading its API functions */ 46 | if (!import_sample()) { //<---pysample.h:21 47 | return NULL; 48 | } 49 | 50 | return m; 51 | } 52 | -------------------------------------------------------------------------------- /with_C_API/pysample.c: -------------------------------------------------------------------------------- 1 | /* pysample.c */ 2 | 3 | #include "Python.h" 4 | #define PYSAMPLE_MODULE //<---pysample.h:16 5 | #include "pysample.h" 6 | 7 | /* int gcd(int, int) */ 8 | static PyObject *py_gcd(PyObject *self, PyObject *args) { 9 | int x, y, result; 10 | 11 | if (!PyArg_ParseTuple(args,"ii", &x, &y)) { 12 | return NULL; 13 | } 14 | result = gcd(x,y); 15 | return Py_BuildValue("i", result); 16 | } 17 | 18 | /* int in_mandel(double, double, int) */ 19 | static PyObject *py_in_mandel(PyObject *self, PyObject *args) { 20 | double x0, y0; 21 | int n; 22 | int result; 23 | 24 | if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) { 25 | return NULL; 26 | } 27 | result = in_mandel(x0,y0,n); 28 | return Py_BuildValue("i", result); 29 | } 30 | 31 | /* int divide(int, int, int *) */ 32 | static PyObject *py_divide(PyObject *self, PyObject *args) { 33 | int a, b, quotient, remainder; 34 | if (!PyArg_ParseTuple(args, "ii", &a, &b)) { 35 | return NULL; 36 | } 37 | quotient = divide(a,b, &remainder); 38 | return Py_BuildValue("(ii)", quotient, remainder); 39 | } 40 | 41 | /* Call double avg(double *, int) */ 42 | static PyObject *py_avg(PyObject *self, PyObject *args) { 43 | PyObject *bufobj; 44 | Py_buffer view; 45 | double result; 46 | /* Get the passed Python object */ 47 | // 在一个C对象指针中储存一个Python对象(没有任何转换)。 48 | // 因此,C程序接收传递的实际对象。对象的引用计数没有增加。 49 | // 存储的指针不是空的 50 | if (!PyArg_ParseTuple(args, "O", &bufobj)) { 51 | return NULL; 52 | } 53 | 54 | /* Attempt to extract buffer information from it */ 55 | 56 | if (PyObject_GetBuffer(bufobj, &view, 57 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { 58 | return NULL; 59 | } 60 | 61 | if (view.ndim != 1) { 62 | PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array"); 63 | PyBuffer_Release(&view); 64 | return NULL; 65 | } 66 | 67 | /* Check the type of items in the array */ 68 | if (strcmp(view.format,"d") != 0) { 69 | PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); 70 | PyBuffer_Release(&view); 71 | return NULL; 72 | } 73 | 74 | /* Pass the raw buffer and size to the C function */ 75 | result = avg(view.buf, view.shape[0]); 76 | 77 | /* Indicate we're done working with the buffer */ 78 | PyBuffer_Release(&view); 79 | return Py_BuildValue("d", result); 80 | } 81 | 82 | 83 | /* Destructor function for points */ 84 | static void del_Point(PyObject *obj) { 85 | printf("Deleting point\n"); 86 | free(PyCapsule_GetPointer(obj,"Point")); 87 | } 88 | 89 | static PyObject *PyPoint_FromPoint(Point *p, int must_free) { 90 | /* 胶囊和C指针类似。在内部,它们获取一个通用指针和一个名称,可以使用 91 | PyCapsule_New() 函数很容易的被创建。 另外,一个可选的析构函数能被 92 | 绑定到胶囊上,用来在胶囊对象被垃圾回收时释放底层的内存*/ 93 | return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); 94 | } 95 | 96 | /* Utility functions */ 97 | static Point *PyPoint_AsPoint(PyObject *obj) { 98 | return (Point *) PyCapsule_GetPointer(obj, "Point"); 99 | } 100 | 101 | static _PointAPIMethods _point_api = { 102 | PyPoint_AsPoint, 103 | PyPoint_FromPoint 104 | }; 105 | 106 | /* Create a new Point object */ 107 | static PyObject *py_Point(PyObject *self, PyObject *args) { 108 | 109 | Point *p; 110 | double x,y; 111 | if (!PyArg_ParseTuple(args,"dd",&x,&y)) { 112 | return NULL; 113 | } 114 | p = (Point *) malloc(sizeof(Point)); 115 | p->x = x; 116 | p->y = y; 117 | return PyPoint_FromPoint(p, 1); 118 | } 119 | 120 | static PyObject *py_distance(PyObject *self, PyObject *args) { 121 | Point *p1, *p2; 122 | PyObject *py_p1, *py_p2; 123 | double result; 124 | 125 | if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { 126 | return NULL; 127 | } 128 | if (!(p1 = PyPoint_AsPoint(py_p1))) { 129 | return NULL; 130 | } 131 | if (!(p2 = PyPoint_AsPoint(py_p2))) { 132 | return NULL; 133 | } 134 | result = distance(p1,p2); 135 | return Py_BuildValue("d", result); 136 | } 137 | 138 | // void clip(double *a, int n, double min, double max, double *out); 139 | static PyObject *py_clip(PyObject *self, PyObject *args){ 140 | PyObject *a, *out; 141 | int min, max; 142 | if(!PyArg_ParseTuple(args, "OiiO", &a, &min, &max, &out)){ 143 | return NULL; 144 | } 145 | 146 | // printf("%i, %i\n", min, max); 147 | Py_buffer view_a, view_out; 148 | if (PyObject_GetBuffer(a, &view_a, 149 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { 150 | return NULL; 151 | } 152 | if (PyObject_GetBuffer(out, &view_out, 153 | PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { 154 | return NULL; 155 | } 156 | clip(view_a.buf, view_a.shape[0], min, max, view_out.buf); 157 | PyBuffer_Release(&view_a); 158 | PyBuffer_Release(&view_out); 159 | return Py_BuildValue(""); 160 | } 161 | 162 | #include "Python.h" 163 | 164 | /* Execute func(x,y) in the Python interpreter. The 165 | arguments and return result of the function must 166 | be Python floats */ 167 | 168 | double call_func(PyObject *func, double x, double y) { 169 | PyObject *args; 170 | PyObject *kwargs; 171 | PyObject *result = 0; 172 | double retval; 173 | 174 | /* Make sure we own the GIL */ 175 | PyGILState_STATE state = PyGILState_Ensure(); 176 | 177 | /* Verify that func is a proper callable */ 178 | /* 你必须先有一个表示你将要调用的Python可调用对象。 这可以是一个函数、 179 | 类、方法、内置方法或其他任意实现了 __call__() 操作的东西。 为了确 180 | 保是可调用的,可以像下面的代码这样利用 PyCallable_Check() 做检查 */ 181 | if (!PyCallable_Check(func)) { 182 | fprintf(stderr,"call_func: expected a callable\n"); 183 | goto fail; 184 | } 185 | /* Build arguments */ 186 | /* 使用 Py_BuildValue()构建参数元组或字典 */ 187 | args = Py_BuildValue("(dd)", x, y); 188 | kwargs = NULL; 189 | 190 | /* Call the function */ 191 | /* 使用 PyObject_Call(),传一个可调用对象给它、一个参数元组 192 | 和一个可选的关键字字典。 193 | 如果没有关键字参数,传递NULL */ 194 | result = PyObject_Call(func, args, kwargs); 195 | /* 需要确保使用了 Py_DECREF() 或者 Py_XDECREF() 清理参数。 196 | 第二个函数相对安全点,因为它允许传递NULL指针(直接忽略它), 197 | 这也是为什么我们使用它来清理可选的关键字参数。 */ 198 | Py_DECREF(args); 199 | Py_XDECREF(kwargs); 200 | 201 | /* Check for Python exceptions (if any) */ 202 | /* 调用万Python函数之后,用PyErr_Occurred() 函数检查是否 203 | 有异常发生 */ 204 | if (PyErr_Occurred()) { 205 | PyErr_Print(); 206 | goto fail; 207 | } 208 | 209 | /* Verify the result is a float object */ 210 | if (!PyFloat_Check(result)) { 211 | fprintf(stderr,"call_func: callable didn't return a float\n"); 212 | goto fail; 213 | } 214 | 215 | /* Create the return value */ 216 | retval = PyFloat_AsDouble(result); 217 | Py_DECREF(result); 218 | 219 | /* Restore previous GIL state and return */ 220 | PyGILState_Release(state); 221 | return retval; 222 | 223 | fail: 224 | Py_XDECREF(result); 225 | PyGILState_Release(state); 226 | abort(); // Change to something more appropriate 227 | } 228 | 229 | /* Extension function for testing the C-Python callback */ 230 | static PyObject *py_call_func(PyObject *self, PyObject *args) { 231 | PyObject *func; 232 | 233 | double x, y, result; 234 | if (!PyArg_ParseTuple(args,"Odd", &func,&x,&y)) { 235 | return NULL; 236 | } 237 | result = call_func(func, x, y); 238 | return Py_BuildValue("d", result); 239 | } 240 | 241 | /* Module method table */ 242 | static PyMethodDef SampleMethods[] = { 243 | {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"}, 244 | {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"}, 245 | {"divide", py_divide, METH_VARARGS, "Integer division"}, 246 | {"avg", py_avg, METH_VARARGS, "Average values in an array"}, 247 | {"Point", py_Point, METH_VARARGS}, 248 | {"distance", py_distance, METH_VARARGS, "Function involving a C data structure"}, 249 | {"clip", py_clip, METH_VARARGS, "clip array"}, 250 | {"call_func", py_call_func, METH_VARARGS, "Extension function for testing the C-Python callback"}, 251 | { NULL, NULL, 0, NULL} 252 | }; 253 | 254 | /* Module structure */ 255 | static struct PyModuleDef samplemodule = { 256 | PyModuleDef_HEAD_INIT, 257 | 258 | "sample", /* name of module */ 259 | "A sample module", /* Doc string (may be NULL) */ 260 | -1, /* Size of per-interpreter state or -1 */ 261 | SampleMethods /* Method table */ 262 | }; 263 | 264 | /* Module initialization function */ 265 | PyMODINIT_FUNC 266 | PyInit_sample(void) { 267 | PyObject *m; 268 | PyObject *py_point_api; 269 | 270 | m = PyModule_Create(&samplemodule); 271 | if (m == NULL) 272 | return NULL; 273 | 274 | /* Add the Point C API functions */ 275 | py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL); //<---pysample.h:23 276 | if (py_point_api) { 277 | PyModule_AddObject(m, "_point_api", py_point_api); 278 | } 279 | return m; 280 | } -------------------------------------------------------------------------------- /with_C_API/pysample.h: -------------------------------------------------------------------------------- 1 | /* pysample.h */ 2 | #include "Python.h" 3 | #include "sample.h" 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* Public API Table */ 9 | /* 这里最重要的部分是函数指针表 _PointAPIMethods. 10 | 它会在导出模块时被初始化,然后导入模块时被查找到。 */ 11 | typedef struct { 12 | Point *(*aspoint)(PyObject *); 13 | PyObject *(*frompoint)(Point *, int); 14 | } _PointAPIMethods; 15 | 16 | #ifndef PYSAMPLE_MODULE //<---pysample.c:4 17 | /* Method table in external module */ 18 | static _PointAPIMethods *_point_api = 0; 19 | 20 | /* Import the API table from sample, import_sample() 被用来指向胶囊导入并初始化这个指针 */ 21 | static int import_sample(void) { //<---ptexample.c:46 22 | // 需提供属性名(比如sample._point_api),会一次性找到胶囊对象并提取出指针来。 23 | _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0); //<---pysample.c:250 24 | return (_point_api != NULL) ? 1 : 0; 25 | } 26 | 27 | /* Macros to implement the programming interface */ 28 | #define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj) 29 | #define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj) 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /with_C_API/sample.c: -------------------------------------------------------------------------------- 1 | /* sample.c */ 2 | #include "sample.h" 3 | 4 | /* Compute the greatest common divisor */ 5 | int gcd(int x, int y) { 6 | int g = y; 7 | while (x > 0) { 8 | g = x; 9 | x = y % x; 10 | y = g; 11 | } 12 | return g; 13 | } 14 | 15 | /* Test if (x0,y0) is in the Mandelbrot set or not */ 16 | int in_mandel(double x0, double y0, int n) { 17 | double x=0,y=0,xtemp; 18 | while (n > 0) { 19 | xtemp = x*x - y*y + x0; 20 | y = 2*x*y + y0; 21 | x = xtemp; 22 | n -= 1; 23 | if (x*x + y*y > 4) return 0; 24 | } 25 | return 1; 26 | } 27 | 28 | /* Divide two numbers */ 29 | int divide(int a, int b, int *remainder) { 30 | int quot = a / b; 31 | *remainder = a % b; 32 | return quot; 33 | } 34 | 35 | /* Average values in an array */ 36 | double avg(double *a, int n) { 37 | int i; 38 | double total = 0.0; 39 | for (i = 0; i < n; i++) { 40 | total += a[i]; 41 | } 42 | return total / n; 43 | } 44 | 45 | /* Function involving a C data structure */ 46 | double distance(Point *p1, Point *p2) { 47 | return hypot(p1->x - p2->x, p1->y - p2->y); 48 | } 49 | 50 | /* n:longth of array */ 51 | void clip(double *a, int n, double min, double max, double *out) { 52 | double x; 53 | for (; n >= 0; n--, a++, out++) { 54 | x = *a; 55 | 56 | *out = x > max ? max : (x < min ? min : x); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /with_C_API/sample.h: -------------------------------------------------------------------------------- 1 | /* sample.h */ 2 | 3 | #include 4 | 5 | extern int gcd(int, int); 6 | extern int in_mandel(double x0, double y0, int n); 7 | extern int divide(int a, int b, int *remainder); 8 | extern double avg(double *a, int n); 9 | 10 | typedef struct Point { 11 | double x,y; 12 | } Point; 13 | 14 | extern double distance(Point *p1, Point *p2); 15 | -------------------------------------------------------------------------------- /with_C_API/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | from distutils.core import setup, Extension 3 | 4 | setup(name='ptexample', 5 | ext_modules=[ 6 | Extension('ptexample', 7 | ['ptexample.c'])] 8 | ) 9 | -------------------------------------------------------------------------------- /with_C_API/setup_sample.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | setup(name='sample', 4 | ext_modules=[ 5 | Extension('sample', 6 | ['sample.c','pysample.c'])] 7 | ) 8 | -------------------------------------------------------------------------------- /with_Cython/csample.pxd: -------------------------------------------------------------------------------- 1 | # csample.pxd 2 | # 3 | # Declarations of "external" C functions and structures 4 | 5 | cdef extern from "sample.h": 6 | int gcd(int, int) 7 | bint in_mandel(double, double, int) 8 | int divide(int, int, int *) 9 | double avg(double *, int) nogil 10 | 11 | ctypedef struct Point: 12 | double x 13 | double y 14 | 15 | double distance(Point *, Point *) -------------------------------------------------------------------------------- /with_Cython/sample.cpython-36m-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hellcatzm/Extend_Python/392742e05939c3741350203311c97eab74190846/with_Cython/sample.cpython-36m-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /with_Cython/sample.h: -------------------------------------------------------------------------------- 1 | #ifndef __SAMPLE_H__ 2 | #define __SAMPLE_H__ 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | int gcd(int x, int y); 10 | int in_mandel(double x0, double y0, int n); 11 | int divide(int a, int b, int *remainder); 12 | double avg(double *a, int n); 13 | 14 | /* A C data structure */ 15 | typedef struct Point { 16 | double x,y; 17 | } Point; 18 | 19 | double distance(Point *p1, Point *p2); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | #endif 25 | -------------------------------------------------------------------------------- /with_Cython/sample.pyx: -------------------------------------------------------------------------------- 1 | # sample.pyx 2 | 3 | # Import the low-level C declarations 4 | cimport csample 5 | 6 | # Import some functionality from Python and the C stdlib 7 | from cpython.pycapsule cimport * 8 | 9 | from libc.stdlib cimport malloc, free 10 | 11 | # Wrappers 12 | # def gcd(unsigned int x, unsigned int y): 13 | # return csample.gcd(x, y) 14 | def gcd(int x, int y): 15 | if x <= 0: 16 | raise ValueError("x must be > 0") 17 | if y <= 0: 18 | raise ValueError("y must be > 0") 19 | return csample.gcd(x,y) 20 | 21 | def in_mandel(x, y, unsigned int n): 22 | return csample.in_mandel(x, y, n) 23 | 24 | def divide(x, y): 25 | cdef int rem 26 | quot = csample.divide(x, y, &rem) 27 | return quot, rem 28 | 29 | def avg(double[:] a): 30 | cdef: 31 | int sz 32 | double result 33 | 34 | sz = a.size 35 | with nogil: 36 | result = csample.avg( &a[0], sz) 37 | return result 38 | 39 | # Destructor for cleaning up Point objects 40 | cdef del_Point(object obj): 41 | pt = PyCapsule_GetPointer(obj,"Point") 42 | free( pt) 43 | 44 | # Create a Point object and return as a capsule 45 | # def Point(double x,double y): 46 | # cdef csample.Point *p 47 | # p = malloc(sizeof(csample.Point)) 48 | # if p == NULL: 49 | # raise MemoryError("No memory to make a Point") 50 | # p.x = x 51 | # p.y = y 52 | # return PyCapsule_New(p,"Point",del_Point) 53 | 54 | # def distance(p1, p2): 55 | # pt1 = PyCapsule_GetPointer(p1,"Point") 56 | # pt2 = PyCapsule_GetPointer(p2,"Point") 57 | # return csample.distance(pt1,pt2) 58 | cdef class Point: 59 | cdef csample.Point *_c_point 60 | def __cinit__(self, double x, double y): 61 | self._c_point = malloc(sizeof(csample.Point)) 62 | self._c_point.x = x 63 | self._c_point.y = y 64 | 65 | def __dealloc__(self): 66 | free(self._c_point) 67 | 68 | property x: 69 | def __get__(self): 70 | return self._c_point.x 71 | def __set__(self, value): 72 | self._c_point.x = value 73 | 74 | property y: 75 | def __get__(self): 76 | return self._c_point.y 77 | def __set__(self, value): 78 | self._c_point.y = value 79 | 80 | def distance(Point p1, Point p2): 81 | return csample.distance(p1._c_point, p2._c_point) -------------------------------------------------------------------------------- /with_Cython/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | ext_modules = [ 6 | Extension('sample', 7 | 8 | ['sample.pyx'], 9 | libraries=['sample'], 10 | library_dirs=['.'])] 11 | setup( 12 | name = 'Sample extension module', 13 | cmdclass = {'build_ext': build_ext}, 14 | ext_modules = ext_modules 15 | ) -------------------------------------------------------------------------------- /with_ctypes/README.md: -------------------------------------------------------------------------------- 1 | #### 资料原文 2 | [使用ctypes访问C代码](http://python3-cookbook.readthedocs.io/zh_CN/latest/c15/p01_access_ccode_using_ctypes.html) 3 | #### 介绍博文 4 | [『Python CoolBook』使用ctypes访问C代码_上_用法讲解](https://www.cnblogs.com/hellcat/p/9058330.html)
5 | [『Python CoolBook』使用ctypes访问C代码_下_demo进阶](https://www.cnblogs.com/hellcat/p/9078321.html)
6 | -------------------------------------------------------------------------------- /with_ctypes/sample.c: -------------------------------------------------------------------------------- 1 | /* sample.c */ 2 | #include "sample.h" 3 | 4 | /* Compute the greatest common divisor */ 5 | int gcd(int x, int y) { 6 | int g = y; 7 | while (x > 0) { 8 | g = x; 9 | x = y % x; 10 | y = g; 11 | } 12 | return g; 13 | } 14 | 15 | /* Test if (x0,y0) is in the Mandelbrot set or not */ 16 | int in_mandel(double x0, double y0, int n) { 17 | double x=0,y=0,xtemp; 18 | while (n > 0) { 19 | xtemp = x*x - y*y + x0; 20 | y = 2*x*y + y0; 21 | x = xtemp; 22 | n -= 1; 23 | if (x*x + y*y > 4) return 0; 24 | } 25 | return 1; 26 | } 27 | 28 | /* Divide two numbers */ 29 | int divide(int a, int b, int *remainder) { 30 | int quot = a / b; 31 | *remainder = a % b; 32 | return quot; 33 | } 34 | 35 | /* Average values in an array */ 36 | double avg(double *a, int n) { 37 | int i; 38 | double total = 0.0; 39 | for (i = 0; i < n; i++) { 40 | total += a[i]; 41 | } 42 | return total / n; 43 | } 44 | 45 | /* Function involving a C data structure */ 46 | double distance(Point *p1, Point *p2) { 47 | return hypot(p1->x - p2->x, p1->y - p2->y); 48 | } 49 | -------------------------------------------------------------------------------- /with_ctypes/sample.h: -------------------------------------------------------------------------------- 1 | #ifndef __SAMPLE_H__ 2 | #define __SAMPLE_H__ 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | int gcd(int x, int y); 10 | int in_mandel(double x0, double y0, int n); 11 | int divide(int a, int b, int *remainder); 12 | double avg(double *a, int n); 13 | 14 | /* A C data structure */ 15 | typedef struct Point { 16 | double x,y; 17 | } Point; 18 | 19 | double distance(Point *p1, Point *p2); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | #endif 25 | -------------------------------------------------------------------------------- /with_ctypes/sample.py: -------------------------------------------------------------------------------- 1 | # sample.py 2 | import ctypes 3 | import os 4 | 5 | # Try to locate the .so file in the same directory as this file 6 | _file = 'libsample.so' 7 | _path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,))) 8 | _mod = ctypes.cdll.LoadLibrary(_path) 9 | 10 | # int gcd(int, int) 11 | gcd = _mod.gcd 12 | gcd.argtypes = (ctypes.c_int, ctypes.c_int) 13 | gcd.restype = ctypes.c_int 14 | 15 | # int in_mandel(double, double, int) 16 | in_mandel = _mod.in_mandel 17 | in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int) 18 | in_mandel.restype = ctypes.c_int 19 | 20 | # int divide(int, int, int *) 21 | _divide = _mod.divide 22 | _divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int)) 23 | _divide.restype = ctypes.c_int 24 | 25 | def divide(x, y): 26 | rem = ctypes.c_int() 27 | quot = _divide(x, y, rem) 28 | 29 | return quot,rem.value 30 | 31 | # void avg(double *, int n) 32 | # Define a special type for the 'double *' argument 33 | class DoubleArrayType: 34 | def from_param(self, param): 35 | typename = type(param).__name__ 36 | if hasattr(self, 'from_' + typename): 37 | return getattr(self, 'from_' + typename)(param) 38 | elif isinstance(param, ctypes.Array): 39 | return param 40 | else: 41 | raise TypeError("Can't convert %s" % typename) 42 | 43 | # Cast from array.array objects 44 | def from_array(self, param): 45 | if param.typecode != 'd': 46 | raise TypeError('must be an array of doubles') 47 | ptr, _ = param.buffer_info() 48 | return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double)) 49 | 50 | # Cast from lists/tuples 51 | def from_list(self, param): 52 | val = ((ctypes.c_double)*len(param))(*param) 53 | return val 54 | 55 | from_tuple = from_list 56 | 57 | # Cast from a numpy array 58 | def from_ndarray(self, param): 59 | return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) 60 | 61 | DoubleArray = DoubleArrayType() 62 | _avg = _mod.avg 63 | _avg.argtypes = (DoubleArray, ctypes.c_int) 64 | _avg.restype = ctypes.c_double 65 | 66 | def avg(values): 67 | return _avg(values, len(values)) 68 | 69 | # struct Point { } 70 | class Point(ctypes.Structure): 71 | _fields_ = [('x', ctypes.c_double), 72 | ('y', ctypes.c_double)] 73 | 74 | # double distance(Point *, Point *) 75 | distance = _mod.distance 76 | distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point)) 77 | distance.restype = ctypes.c_double 78 | --------------------------------------------------------------------------------