使用C程序扩展Python

前面的一篇文章“试用PycURL”写到了PycURL这个库,当时就在想它是如何包装的,Python如何直接调用了pycurl.so这个C编写的共享库呢。所以,有此文对该问题进行简单的描述。

尽管我非常认同“Life is short, use Python.”,但有时候也需要用C程序来扩展Python程序。
什么情况下需要用C程序来扩展Python呢?就我肤浅的理解,至少有如下场景:
1. 效率问题:尽管Python在编写程序的效率上非常高,其执行效率也不错,不过有些对性能非常关键的地方,还是有需求用C代码来实现,然后在Python中调用C程序,这样性能可能会更高一些。
2. 充分利用优秀的C函数库:C语言世界中有些非常优秀的函数库(如:前面提及的libcurl),它们功能强大、性能优秀,如果能将这些C函数库进行简单的包装,然后就可以在Python中轻松调用,这样就非常好了。

根据官方指导文档,我写了个jay这个C扩展模块,里面有一个方法hello(用于给一个人打招呼^_^);需要一个C文件实现该模块的功能,需要一个setup.py脚本实现模块的编译安装部署。(setup.py是不是很熟悉呢? 在几乎每个Python库的源代码中都这个文件…)源代码都可以在:https://github.com/smilejay/python/tree/master/py2013 处直接获取。


1. C程序文件jaymodule.c的内容如下:(不详细解释其原理了,看文末的参考文档吧,我也担心我解释不清楚 ^_^)

#include 

static PyObject * jay_hello(PyObject *self, PyObject *args) {
        const char *person;
        int sts;
        if (!PyArg_ParseTuple(args, "s", &person))
                return NULL;
        printf("Hello, %s. I'm Jay.\n", person );
        return Py_None;
/*
        sts = printf("Hello, %s. I'm Jay.\n", person );
        return Py_BuildValue("i", sts);
*/
}

static PyMethodDef JayMethods[] = {
        {"hello",  jay_hello, METH_VARARGS,
        "Say hello to a person from Jay."},
        {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC initjay(void) {
        PyImport_AddModule("jay");
        (void) Py_InitModule("jay", JayMethods);
}


int main(int argc, char *argv[]) {
        /* Pass argv[0] to the Python interpreter */
        Py_SetProgramName(argv[0]);

        /* Initialize the Python interpreter.  Required. */
        Py_Initialize();

        /* Add a static module
        Py_Exit(0);
}

2. setup.py这个构建和安装该模块的Python脚本如下:

from distutils.core import setup, Extension

module1 = Extension('jay',
                    sources = ['jaymodule.c'])

setup(name = 'jay',
      version = '1.0',
      description = 'This is a demo package from Jay.',
      ext_modules = [module1])

3. 构建和安装方法很简单,就是传统的“sudo python setup.py install”:

jay@jay-linux:~/workspace/python.git/py2013$ sudo python setup.py install
running install
running build
running build_ext
running install_lib
copying build/lib.linux-x86_64-2.7/jay.so -> /usr/local/lib/python2.7/dist-packages
running install_egg_info
Removing /usr/local/lib/python2.7/dist-packages/jay-1.0.egg-info
Writing /usr/local/lib/python2.7/dist-packages/jay-1.0.egg-info

4. 在Python中演示jay这个模块的效果,如下:

jay@jay-linux:~$ python
Python 2.7.3 (default, Sep 26 2013, 20:03:06) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import jay
>>> jay.hello('Tom')
Hello, Tom. I'm Jay.
>>> 

这里的演示,是让我的C模块能够在Python中动态加载;如果你有将这个库作为built-in的模块,直接加载Python解释器中,那么编译安装的方式有点不同,需要将自己的扩展模块加入到Python解释器的源码中,然后重新编译Python解释器。可参考官方文档:compilation-and-linkage

当然,这里是用Python2.7做演示的,Python3中的C扩展也是类似的(略有不同),如果你希望进一步了解Python3上的写法,请参考:http://docs.python.org/3/extending/extending.html

参考资料:
http://docs.python.org/2/extending/
http://docs.python.org/2/extending/extending.html#a-simple-example
http://docs.python.org/2/extending/building.html#building
http://docs.python.org/2/c-api/

master

Stay hungry, stay foolish.

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

*