前面的一篇文章“试用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的内容如下:(不详细解释其原理了,看文末的参考文档吧,我也担心我解释不清楚 ^_^)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <Python.h> 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脚本如下:
1 2 3 4 5 6 7 8 9 |
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”:
1 2 3 4 5 6 7 8 9 |
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这个模块的效果,如下:
1 2 3 4 5 6 7 8 |
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/