开发者社区> 问答> 正文

简单的C扩展模块

你想不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块。

展开
收起
哦哦喔 2020-04-17 18:10:34 2581 0
2 条回答
写回答
取消 提交回答
  • 有点尴尬唉 你要寻找的东西已经被吃掉啦!
    这也是一个当你面临从Python 2 到 Python 3扩展代码的问题。
    虽然Python提供了一个广泛的编程API,实际上有很多方法来处理C的代码。
    相比试图给出对于每一个可能的工具或技术的详细参考,
    我么采用的是是集中在一个小片段的C++代码,以及一些有代表性的例子来展示如何与代码交互。
    这个目标是提供一系列的编程模板,有经验的程序员可以扩展自己的使用。
    
    这里是我们将在大部分秘籍中工作的代码:
    /* sample.c */_method
    #include <math.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 two 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;
    }
     
    /* 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, p1->y - p2->y);
    }
    
    这段代码包含了多种不同的C语言编程特性。
    首先,这里有很多函数比如 gcd() 和 is_mandel() 。
    divide() 函数是一个返回多个值的C函数例子,其中有一个是通过指针参数的方式。
    avg() 函数通过一个C数组执行数据聚集操作。Point 和 distance() 函数涉及到了C结构体。
    
    对于接下来的所有小节,先假定上面的代码已经被写入了一个名叫“sample.c”的文件中,
    然后它们的定义被写入一个名叫“sample.h”的头文件中,
    并且被编译为一个库叫“libsample”,能被链接到其他C语言代码中。
    编译和链接的细节依据系统的不同而不同,但是这个不是我们关注的。
    
    2020-04-19 20:55:35
    赞同 展开评论 打赏
  • 对于简单的C代码,构建一个自定义扩展模块是很容易的。 作为第一步,你需要确保你的C代码有一个正确的头文件。例如:
    
    /* sample.h */
    
    #include <math.h>
    
    extern int gcd(int, int);
    extern int in_mandel(double x0, double y0, int n);
    extern int divide(int a, int b, int *remainder);
    extern double avg(double *a, int n);
    
    typedef struct Point {
        double x,y;
    } Point;
    
    extern double distance(Point *p1, Point *p2);
    通常来讲,这个头文件要对应一个已经被单独编译过的库。 有了这些,下面我们演示下编写扩展函数的一个简单例子:
    
    #include "Python.h"
    #include "sample.h"
    
    /* int gcd(int, int) */
    static PyObject *py_gcd(PyObject *self, PyObject *args) {
      int x, y, result;
    
      if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
        return NULL;
      }
      result = gcd(x,y);
      return Py_BuildValue("i", result);
    }
    
    /* int in_mandel(double, double, int) */
    static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
      double x0, y0;
      int n;
      int result;
    
      if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
        return NULL;
      }
      result = in_mandel(x0,y0,n);
      return Py_BuildValue("i", result);
    }
    
    /* int divide(int, int, int *) */
    static PyObject *py_divide(PyObject *self, PyObject *args) {
      int a, b, quotient, remainder;
      if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
      }
      quotient = divide(a,b, &remainder);
      return Py_BuildValue("(ii)", quotient, remainder);
    }
    
    /* Module method table */
    static PyMethodDef SampleMethods[] = {
      {"gcd",  py_gcd, METH_VARARGS, "Greatest common divisor"},
      {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
      {"divide", py_divide, METH_VARARGS, "Integer division"},
      { NULL, NULL, 0, NULL}
    };
    
    /* Module structure */
    static struct PyModuleDef samplemodule = {
      PyModuleDef_HEAD_INIT,
    
      "sample",           /* name of module */
      "A sample module",  /* Doc string (may be NULL) */
      -1,                 /* Size of per-interpreter state or -1 */
      SampleMethods       /* Method table */
    };
    
    /* Module initialization function */
    PyMODINIT_FUNC
    PyInit_sample(void) {
      return PyModule_Create(&samplemodule);
    }
    要绑定这个扩展模块,像下面这样创建一个 setup.py 文件:
    
    # setup.py
    from distutils.core import setup, Extension
    
    setup(name='sample',
          ext_modules=[
            Extension('sample',
                      ['pysample.c'],
                      include_dirs = ['/some/dir'],
                      define_macros = [('FOO','1')],
                      undef_macros = ['BAR'],
                      library_dirs = ['/usr/local/lib'],
                      libraries = ['sample']
                      )
            ]
    )
    为了构建最终的函数库,只需简单的使用 python3 buildlib.py build_ext --inplace 命令即可:
    
    bash % python3 setup.py build_ext --inplace
    running build_ext
    building 'sample' extension
    gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
     -I/usr/local/include/python3.3m -c pysample.c
     -o build/temp.macosx-10.6-x86_64-3.3/pysample.o
    gcc -bundle -undefined dynamic_lookup
    build/temp.macosx-10.6-x86_64-3.3/pysample.o \
     -L/usr/local/lib -lsample -o sample.so
    bash %
    如上所示,它会创建一个名字叫 sample.so 的共享库。当被编译后,你就能将它作为一个模块导入进来了:
    
    >>> import sample
    >>> sample.gcd(35, 42)
    7
    >>> sample.in_mandel(0, 0, 500)
    1
    >>> sample.in_mandel(2.0, 1.0, 500)
    
    0
    >>> sample.divide(42, 8)
    (5, 2)
    >>>
    如果你是在Windows机器上面尝试这些步骤,可能会遇到各种环境和编译问题,你需要花更多点时间去配置。 Python的二进制分发通常使用了Microsoft Visual Studio来构建。 为了让这些扩展能正常工作,你需要使用同样或兼容的工具来编译它。 参考相应的 Python文档
    
    2020-04-17 18:10:44
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
使用C++11开发PHP7扩展 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载