前些日子在研究gevent的时候,发现它的core模块,即事件驱动是libevent的python扩展(pyevent),而我发现了.pyx的后缀名,google一番才知道那是cython的语法,基本上是python和c/c++混编,可以很轻松的使用c中已有的库,突然发现世界如此美好,用c写python模块相当的类,还需要注意引用等问题,但用cython这些都不是问题,它让python轻轻松松的调用c库,也让c轻轻松松的调用python库,cython一出,谁与争锋。
这是我对[Learning Cython Programming]一书的翻译,这是我上传的下载地址:http://download.csdn.net/detail/yueguanghaidao/7109509。
在翻译过程中也发现了一些错误,这是我维护的一个勘误表:http://blog.csdn.net/yueguanghaidao/article/details/22352913
第一章的名字叫Cython Won't Bite,我实在想不出好的中文代替,如果哪位童鞋有好意见,欢迎指出。
Cython让编写Python C扩展就像Python本身一样容易。在社区的主要用在数学软件包SAGE中,用于执行快速可扩展的计算。最为显著的是,它通过自动生成所需要的代码为Python提供了一种安全可维护的方式来编译本地模块。
个人而言,我用Cython控制遗留下来的系统,它是使用C/C++实现的,增加功能相当痛苦;我们可以使用Cython生成绑定关系,使本地应用和Python可以一起工作!使用这些,你可以充分利用原生系统的功能,让Python去做高层逻辑的东西。
在最近几年,Python已经成为了软件工程中的一个伟大的例外;它能够使用低成本的开发时间来创建和扩展你能想到的任何软件系统。从分布式系统到高层web应用,Python都可以胜任。这本书将展示如何从Python中获取更多知识。假如你并不知道,Python可以通过原生的C/C++代码使用PyObject或C类型的扩展模块来扩展。手动的这么做并不是一个好主意,因为你需要了解Python内部是如何工作的。例如,你需要了解垃圾回收,不然你的Python对象将得不到系统回收。这也就是为什么Cython会来到这里,它将自动产生所有对C Python API必要和正确的包裹函数。
这本书不是什么
在这本书中,我假设你有编程经验,而且知道C和Python,更为重要的是,你应该熟悉C编译和链接过程来创建共享库和可执行文件。这对Cython来说很重要,因为你在网上看到的项目例子一般都是对非常小的单个Cython文件处理的,这些对大多数人来说帮助并不是很大。我期望读完本书你将熟悉Cython。联机文档将提供所有你读到的参考。
* Windows - 虽然有很多其它方式可以获得,下面这个维基百科是保持更新的最安全方式:http://wiki.cython.org/InstallingOnWindows.
由于Python emacs模式不正常工作了,这里有一个可用的Cython emacs模式。你可以把Tools/cython-mode.el拷贝到~/.emacs.d目录,然后require 你的~/.emacs文件。
git clone git://github.com/redbrain/cython-book.git
redbrain@gamma:~/workspace/cython-book/chapter1/helloworld$python >>> import helloworld Hello World from cython!我们导入了helloworld,因此这个模块现在是一个有效的可以被装载Python模块。在导入时,我们看到了Cyhon代码的输出信息。不是那么的令人兴奋,但这就是”Hello World"模块。
下图描述了Cython是怎么工作的:
我写了个基本的Makefile,你可以简单的运行“make"命令来编译这些例子。它使用了
setpy.py样式让python处理编译和安装这些模块。下面是手工去做的代码:
cython helloworld.pyx gcc -g -O2 -fpic `python-config --cflags` -c helloworld.c -o helloworld.o gcc -shared -o helloworld.so helloworld.o `python-config --libs`我认为这是用C开发很重要的技能,你将为了如何更简单的分享它而去思考你的代码。
#include <stdio.h> int myfunc(int a,int b) { printf("look we are within your c code!!\n"); return a+b; }这是我们将调用的C代码-仅仅一个增加两个整数的简单函数,你之前很可能见过。现在让Python去调用它。创建一个mycode.h,在里面为Cyhton声明原形如下:
#ifndef __MYCODE_H__ #define __MYCODE_H__ extern int myfunc(int,int); #endif //__MYCODE_H__这样,Cython就能看到我们要调用的函数原形了。事实上,在你自己的项目中你肯定在你的头文件中声明原形了。
cdef extern from "mycode.h": cdef int myfunc(int,int) def callfunc(): print myfunc(1,2)在这个Cython代码中,我们首先必须要声明我们所关心的C代码。cdef是一个关键字,表明这是将被链接的C代码。现在我们已经声明了函数原形的头文件,否则将会导致编译器报“未声明的函数”警告,我们可以构造一个包裹函数。此时,我们将指出Python应该如何调用这个本地代码,因为直接调用C代码是危险的。所以,Cython为我们处理了所有的类型转换问题。一个基本的包裹函数callfunc是需要的-它调用“myfunc"函数并传递整数1和2,然后打印结果。
cython mycodecpy.pyx gcc -g -O2 -fpic -c mycode.c -o mycode.o gcc -g -O2 -fpic -c mycodecpy.c -o mycodecpy.o `python-config --cflags` gcc -shared -o mycodecpy.so mycode.o mycodecpy.o `python-config --clibs`记住,要链接有其它函数的C代码;即mycode.c。如果你对我所说的不熟悉,你需要复习C的编译,每一个C文件编译为一个目标文件,然后将所有的目标文件链接为一个二进制文件。因此,你需要确保你链接了所有需要的目标文件。
redbrain@gamma:~/workspace/cython-book/chapter1/helloworld$python >>> from mycodecpy import callCfunc look we are within your c code!! 3我们已经编译并从Python解释器中调用了我们本地代码。我希望这将给你带来使用它你可以做什么的思考。使用它,你可以让本地模块链接一些本地库,对这些库做绑定是很简单的。
def callcfunc2(int x,int y): print myfunc(x,y)我们给自己定义的包裹函数增加了int参数。这需要Python代码类型安全,而且自动把PyObjects转换为C类型。当你创建一个整型Python对象,类型并不是整形,而是PyObject。如果你希望在C中这么使用,你需要通过Python C API接口获取数值,但是Cython将自动为我们处理。
>>> import mycodecpy >>> mycodepy.callCfunc21,'string') Traceback (most recent call last): File "<stdin>",line 1,in <module> File "mycodecpy.pyx",line 7,in mycodecpy.callCfunc2 (mycodecoy.c:733) def callCfunc2(int x,int y): TypeError: an integer is required即使在你的Python代码中多使用几个Cython中类型安全的C类型,就会获得很大的速度提升和更好的代码。这是因为Cython编译器会更积极的优化,以避免使用Python调用方式。