15.2 简单的C扩展模块

    对于简单的C代码,构建一个自定义扩展模块是很容易的。作为第一步,你需要确保你的C代码有一个正确的头文件。例如:

    通常来讲,这个头文件要对应一个已经被单独编译过的库。有了这些,下面我们演示下编写扩展函数的一个简单例子:

    1. #include "Python.h"
    2. #include "sample.h"
    3.  
    4. /* int gcd(int, int) */
    5. static PyObject *py_gcd(PyObject *self, PyObject *args) {
    6. int x, y, result;
    7.  
    8. if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
    9. return NULL;
    10. }
    11. result = gcd(x,y);
    12. return Py_BuildValue("i", result);
    13. }
    14.  
    15. /* int in_mandel(double, double, int) */
    16. static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
    17. double x0, y0;
    18. int n;
    19. int result;
    20.  
    21. if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
    22. }
    23. result = in_mandel(x0,y0,n);
    24. return Py_BuildValue("i", result);
    25. }
    26.  
    27. /* int divide(int, int, int *) */
    28. static PyObject *py_divide(PyObject *self, PyObject *args) {
    29. int a, b, quotient, remainder;
    30. if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
    31. return NULL;
    32. }
    33. quotient = divide(a,b, &remainder);
    34. return Py_BuildValue("(ii)", quotient, remainder);
    35. }
    36.  
    37. /* Module method table */
    38. static PyMethodDef SampleMethods[] = {
    39. {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
    40. {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
    41. {"divide", py_divide, METH_VARARGS, "Integer division"},
    42. { NULL, NULL, 0, NULL}
    43. };
    44.  
    45. /* Module structure */
    46. PyModuleDef_HEAD_INIT,
    47.  
    48. "sample", /* name of module */
    49. "A sample module", /* Doc string (may be NULL) */
    50. -1, /* Size of per-interpreter state or -1 */
    51. SampleMethods /* Method table */
    52. };
    53.  
    54. /* Module initialization function */
    55. PyMODINIT_FUNC
    56. PyInit_sample(void) {
    57. return PyModule_Create(&samplemodule);
    58. }

    要绑定这个扩展模块,像下面这样创建一个 文件:

    为了构建最终的函数库,只需简单的使用 python3 buildlib.py build_ext —inplace 命令即可:

    1. bash % python3 setup.py build_ext --inplace
    2. running build_ext
    3. building 'sample' extension
    4. gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
    5. -I/usr/local/include/python3.3m -c pysample.c
    6. -o build/temp.macosx-10.6-x86_64-3.3/pysample.o
    7. gcc -bundle -undefined dynamic_lookup
    8. build/temp.macosx-10.6-x86_64-3.3/pysample.o \
    9. -L/usr/local/lib -lsample -o sample.so
    10. bash %

    如果你是在Windows机器上面尝试这些步骤,可能会遇到各种环境和编译问题,你需要花更多点时间去配置。Python的二进制分发通常使用了Microsoft Visual Studio来构建。为了让这些扩展能正常工作,你需要使用同样或兼容的工具来编译它。参考相应的

    在尝试任何手写扩展之前,最好能先参考下Python文档中的扩展和嵌入Python解释器 .Python的C扩展API很大,在这里整个去讲述它没什么实际意义。不过对于最核心的部分还是可以讨论下的。

    首先,在扩展模块中,你写的函数都是像下面这样的一个普通原型:

    1. static PyObject *py_func(PyObject *self, PyObject *args) {
    2. }

    PyObject 是一个能表示任何Python对象的C数据类型。在一个高级层面,一个扩展函数就是一个接受一个Python对象(在 PyObject args中)元组并返回一个新Python对象的C函数。函数的 参数对于简单的扩展函数没有被使用到,不过如果你想定义新的类或者是C中的对象类型的话就能派上用场了。比如如果扩展函数是一个类的一个方法,那么 self 就能引用那个实例了。

    Py_BuildValue() 函数被用来根据C数据类型创建Python对象。它同样接受一个格式化字符串来指定期望类型。在扩展函数中,它被用来返回结果给Python。 的一个特性是它能构建更加复杂的对象类型,比如元组和字典。在 py_divide() 代码中,一个例子演示了怎样返回一个元组。不过,下面还有一些实例:

    在扩展模块底部,你会发现一个函数表,比如本节中的 SampleMethods 表。这个表可以列出C函数、Python中使用的名字、文档字符串。所有模块都需要指定这个表,因为它在模块初始化时要被使用到。

    最后的函数 PyInit_sample() 是模块初始化函数,但该模块第一次被导入时执行。这个函数的主要工作是在解释器中注册模块对象。

    最后一个要点需要提出来,使用C函数来扩展Python要考虑的事情还有很多,本节只是一小部分。(实际上,C API包含了超过500个函数)。你应该将本节当做是一个入门篇。更多高级内容,可以看看 和 Py_BuildValue() 函数的文档,然后进一步扩展开。