本文简单说明了将C函数封装成python库的过程。

之前写了如何用ctypes模块调用C的共享库,这次我们尝试直接将C程序打包成python的库文件。具体的细节可以查询 python官方文档

前期准备

为了将C函数封装成python库,我们必须在系统中安装python开发包——python-dev,具体方法可以根据系统的不同,自行去网上搜索,我们在这里不再赘述。

如果使用系统原生的python2或者直接安装的python3,可直接略过以下内容。但如果系统安装的是Anaconda,那们在编译C语言时可能会出现以下情况:

fatal error: Python.h: No such file or directory

导致问题的原因在于,Python.h文件在Anaconda文件目录下,不在GCC的默认加载库文件的路径里,所以编译时会导致GCC无法找到头文件。

我们先找到系统中python.h的位置:

sudo find / -name Python.h 2>/dev/null

然后将文件的路径加载入GCC的路径中:

export C_INCLUDE_PATH=对应路径

C程序

接下来我们就需要对原有的c程序进行修饰,完成对于输入和输出数据的格式转换。以下是一个简单的计算斐波那契数列的小程序。头文件代码如下:

/* fib.h */
#ifndef FIB_H_
#define FIB_H_

static int fib(int num);

#endif

主程序代码如下:

/* fib.c */
#include <stdio.h>
#include <Python.h>
#include "fib.h"

/* The function to calculate fibonacci sequence */
static int
fib(int num)
{
    int pre = 0, ppre = 1;
    int media;
    for (int i = 0; i < num; i++) {
        media = pre;
        pre = ppre + pre;
        ppre = media;
    }

    return pre;
}

/* Wrap the original function to python object */
static PyObject *
py_fib(PyObject *self, PyObject *args)
{
    int num;
    int result;

    // transform python object to c int
    if (!PyArg_ParseTuple(args, "i", &num))
        return NULL;
    result = fib(num);
    // transform c int to python object
    return Py_BuildValue("i", result);
}

/* Define the methods in module */
static PyMethodDef fibmethods[] = {
    {"fib", py_fib, METH_VARARGS},
    {NULL, NULL, 0, NULL}
};

/* Define the module */
static struct PyModuleDef fibmodule = {
    PyModuleDef_HEAD_INIT,
    "Fib",   /* name of module */
    NULL,    /* module documentation, may be NULL */
    -1,      /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
    fibmethods
};

/* initialization function */
PyMODINIT_FUNC
PyInit_Fib(void)
{
    return PyModule_Create(&fibmodule);
}

主程序代码主要分为3部分:

  • fib函数:计算斐波那契数列的c函数
  • py_fib函数:对fib函数进行封装,将输入输出值的类型由c的原生类型转换为python类型,函数具体意义可以查询 python C API manual
  • fibmethods,fibmodule,PyInit_Fib:对模块的方法,名字,内容进行定义,并初始化

python库安装

接着我们写python库的安装程序:

from setuptools import setup
from distutils.core import Extension

version = '0.10'
MOD = 'Fib'
source = 'fib.c'

setup(name = MOD,
      version = version,
      description = "a fib sequence calculate package",
      classifiers=[
        "Programming Language :: Python",
        ],
      author = 'Hanfeng Zhang',
      ext_modules = [Extension(MOD, sources = ['fib.c',])],
      install_requires=[
          'setuptools',
      ]
)

用下列命令行安装python库:

python setup.py build
python setup.py install

安装完毕后,进入python,检查新的库能否正常使用:

>>> import Fib
>>> Fib.fib(10)
55

库安装成功!