我的前言:
前些日子在研究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 Wont'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。联机文档将提供所有你读到的参考。
安装Cython
* Fedora - Fedora附带yum包管理工具。因此,你可以简单的运行 yum install Cython
* Ubuntu/Debian - 和Fedora一样,Ubuntu可以通过apt获取包:apt-get install Cython
* Mac - 安装Xcode和命令行工具。然后运行一下命令:
curl -O http://www.cython.org/release/Cython-0.18.tar.gz
tar zxvf Cython-0.18.tar.gz
cd Cython-0.18
sudo python setup.py install
* Windows - 虽然有很多其它方式可以获得,下面这个维基百科是保持更新的最安全方式:http://wiki.cython.org/InstallingOnWindows.
emacs 模式
由于Python emacs模式不正常工作了,这里有一个可用的Cython emacs模式。你可以把Tools/cython-mode.el拷贝到~/.emacs.d目录,然后require 你的~/.emacs文件。
获取源代码
git clone git://github.com/redbrain/cython-book.git
Hello World
cython --version
我们做一个测试并运行经典的“Hello world"程序:
redbrain@gamma:~/workspace/cython-book/chapter1/helloworld$make
我们已经创建了Cython helloworld.so模块!你可以在Python解释器里看到(确保你和helloworld.so模块同一目录)
redbrain@gamma:~/workspace/cython-book/chapter1/helloworld$python
>>> import helloworld
Hello World from cython!
我们导入了helloworld,因此这个模块现在是一个有效的可以被装载Python模块。在导入时,我们看到了Cyhon代码的输出信息。不是那么的令人兴奋,但这就是”Hello World"模块。
我们看看都做了什么;Cyhton文件有.pyx和.pyd扩展名。现在我们关系的是.pyx文件。在本书的后面,我将介绍.pyd的使用和它可以用来做什么。为了清晰,最好理解helloworld.so模块产生的流程。Cython和其它代码生成器工作原理是一样的。
下图描述了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开发很重要的技能,你将为了如何更简单的分享它而去思考你的代码。
你自己的模块
调用你自己的C代码
创建mycode.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就能看到我们要调用的函数原形了。事实上,在你自己的项目中你肯定在你的头文件中声明原形了。
创建mycodecpy.pyx,输入如下代码:
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调用方式。