Python/C 集成

题记

这是一个困扰我很久的问题,Python 之所以为胶水语言,它能够将其他语言如何粘合在一起呢?通过 socket 等接口和其他语言通信明显不充分能够以“胶水”相称,因为还不能够全面达到 1+1>2 的效果。

我们希望我们用比编写 C 省事一半以上,编写出效率能够和 C 相差不远的程序。

那就是我现在亟需得到的答案。

这是《Programming Python》(小动物蟒蛇书)的最后一节《Chapter 20:Python/C Intergration》,让我们掌握这个神技。

扩展和嵌入(Extending and Embedding)

我们现在需要让 Python 和 C 混合(mix),有两种不同的集成模式和 API

扩展接口(Extending Interface)

在 Python 中运行编译的 C 代码库

嵌入接口(Embedding Interface)

在 C 里面运行 Python 代码

前者用于提高执行效率以及实现一些底层操作(例如内存),后者用于程序的定制化(customization),用于节省编译等环节。

注意很多时候这两者会混合使用,而不是那么泾渭分明。例如我们可能在一个 tkinter 界面里面通过 extending 调用了 C/C++,执行完毕之后又通过 embedding 调用回 Python。

简单而言,Python 调用 C/C++ 为扩展,C/C++ 调用 Python 为嵌入。

扩展模式(Extending Python in C)

其实 C/C++ 都是可行的,因为 Python 本身是用 C 实现的。不管如何:编译的 Python 扩展语言可以用两种模式:

C modules

在客户端看来很像 Python 模块的工具库

C types

多实例的对象,行为类似标准的 Python 内置类型和类

C 扩展模块一般会被实现为扁平的函数库,然后打包成一个可以 import 到 python 的模块。

C 扩展类型用来编码用于生成多实例的对象,携带对象级的状态信息,并且可能支持一些类似 Python 类的表达式运算符。

C 扩展类型可以做任何原生 Python 类型和 Python 类可以做的东西,包括:方法调用、累加、索引、切片等等。

为了令接口生效,C 模块和类型必须提供一个”胶水“层来翻译两种语言之间的调用以及数据。这个层注册了用 C 编写的操作,附带了 Python 翻译的 C 函数指针。

简单而言,这个层会将传到 C 的 Python 参数转换成 C,然后在函数返回的时候再转换回 Python 的形式。

然后 Python 用起来就仅仅是 import 一个 C 扩展,就像一个 python 模块一样。因为 C 代码承担了翻译工作,这个接口会非常严密,而且 Python 调用起来非常简单。

注意 C 模块负责将错误传递回 Python,检测 Python API 调用的错误,并且管理由 C 层持有的对象的对象计数和垃圾管理。C 代码里面引用的 Python 对象不会被回收,因为它们的引用会一直存在。

C 模块可能是一直存在到 Python 的链接,或者是动态产生(第一次 import 时),然后这个 C 模块就变成了在 Python 中可用的工具。

举个例子

废话少讲,我们来做一个 C 模块 hello.c,提供一个 message 方法,吃一个字符串进去,加一段然后拉出来。

/********************************************************************
 * A simple C extension module for Python, called "hello"; compile
 * this into a ".so" on python path, import and call hello.message;
 ********************************************************************/

#include <Python.h>
#include <string.h>

/* module functions */
static PyObject *                                 /* returns object */
message(PyObject *self, PyObject *args)           /* self unused in modules */
{                                                 /* args from Python call */
    char *fromPython, result[1024];
    if (! PyArg_Parse(args, "(s)", &fromPython))  /* convert Py -> C */
        return NULL;                              /* null=raise exception */
    else {
        strcpy(result, "Hello, ");                /* build up C string */
        strcat(result, fromPython);               /* add passed Python string */
        return Py_BuildValue("s", result);        /* convert C -> Python */
    }   
}

/* registration table */
static PyMethodDef hello_methods[] = { 
    {"message", message, METH_VARARGS, "func doc"},   /* name, &func, fmt, doc */
    {NULL, NULL, 0, NULL}
};

/* module definition structure */
static struct PyModuleDef hellomodule = { 
    PyModuleDef_HEAD_INIT,
    "hello",        /* name of module */
    "mod doc",      /* module documentation, may be NULL */
    -1,             /* size of per-interpreter module state, -1=in global vars */
    hello_methods   /* link to methods table */
};

/* module initializer */
PyMODINIT_FUNC
PyInit_hello()                          /* called on first import */
{                                       /* name matters if loaded dynamically */
    return PyModule_Create(&hellomodule);
}

原文手抄,一字不差,然后就是编译。。好困。。

#############################################################
# Compile hello.c into a shareable object file on Cygwin,
# to be loaded dynamically when first imported by Python.
#############################################################

PYLIB = /usr/local/bin
PYINC = /usr/local/include/python3

hello.dll: hello.c
        gcc hello.c -g I$(PYINC) -shared -L$(PYLIV) -lpython3 -o hello.dll

clean:
        rm -f hello.dll core

原书里面的 makefile makefile.hello 我也照抄了一遍,可是发现这是 windows,牛头不搭马嘴。

只好各处瞎整,对于不会 make 的我,很是痛苦。

。。。。

http://www.ibm.com/developerworks/cn/linux/l-cn-pythonandc/index.html?ca=drs-cn-0506

http://www.ibm.com/developerworks/cn/linux/l-pythc/

http://blog.csdn.net/hnxymjj/article/details/7980742

原书里面的 make file 是 windows 的,而且对应版本又是 3.1 对我的 ubuntu/py2.7|3.4|3.5 不管用,极度恶心呢。。

最终终于各种折腾,结合上文二和各种乱搞,终于把库给 make 出来了!

gcc -fpic -c I/usr/include/python3.4 -I /usr/lib/python3.4/config hello.c
gcc -shared -o hello.so hello.o

这样 make 出来之后,会得到一个可执行的 hello.so

这个 hello.so 所在的目录里面,就好像有一个 hello.py 模块,我们可以直接在当前目录进入 python 命令行:

$ python3
>>> import hello
>>> hello.message('py2c')
'Hello, py2c'

哈哈,顿时感到其爽无比!终于入门了,这是很重要的一步!睡觉去。


【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2015/12/python-c.html【Python/C 集成】

发表评论

电子邮件地址不会被公开。 必填项已用*标注