前面的一篇文章“试用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/