Cython 是什么编程语言?为什么你有必要学习一下
凯发国际首页入口

  【导语】:这篇文章主要介绍了Cython编程语言,它是Python语言的超集,简而言之:Cython就是具有 C 数据类型的 Python。通过把Cython编译为C语言,运行程序。不仅保留了Python开发方便的特点,还能提升代码的运行速度,非常值得学习。

  Python语言因使用方便、第三方库丰富,得到了许多开发者的青睐。但是Python的缺点也很明显,就是运行速度较慢。为了解决这个问题,Cython出现了,Cython也是一种编程语言,可以把它理解为Python的超集,简而言之:Cython就是具有 C 数据类型的 Python。通过把Cython编译为C语言,可以提升代码的性能。对于使用Python原生对象的代码,效果可能并不明显。但是对于其他没有使用Python对象的代码,或者数值运算等操作而言,效果很不错。

  通过使用Cython,我们不仅可以继续利用Python开发快速的特点,又可以让代码运行速度像C语言一样快。本文我们将介绍Cython的更多细节,同时还将简单使用下它。

  在Python中,我们可以直接调用C语言库,包括通用的C库和Python专用的库。在Cython中,我们还能调用使用了Python语法的C语言库。

  从语法上看,Cython与Python很相似。如果我们用Cython编译器运行一个Python文件(Python 2.x 和 Python 3.x均可),虽然可以正常运行,但是运行速度与使用Python解释器没有区别。假如我们使用Cython中的类型声明语法来修改Python代码,最终编译时就可以把运行速度较慢的Python对象替换为等效的C语言代码。

  注意Cython的使用可以是渐进式的,这意味着开发者要想使用Cython优化原来的Python应用程序,不用刚开始就重写所有代码,只需要更改局部代码就可以。Cython之所以这样设计,是因为在大多数程序中,只有部分代码造成了程序运行慢 - 这可以用帕累托法则解释,也就是“二八准则”。因此Python程序中80%的代码都不用优化,只需对少数关键代码进行优化。我们可以把关键代码用Cython重写,提升性能。其余代码继续使用Python语法,保证代码的简洁性。

  这段代码封装了一个求积分的函数,由于是纯Python写的,需要在本机数值类型和Python内部对象之间来回转换,速度会比较慢。我们使用Cython对以上代码进行重构:

  如果我们对函数形参、函数体中的变量都声明变量类型(double, int等),Cython会把这些代码都编译为C语言。为了提高运行速度,我们还可以用cdef关键字来定义主要用C语言实现的函数,但之后只能用Cython函数来调用这些函数。上面的代码中,只有integrate_f函数可以被Python调用,因为它是使用def关键字定义的,而cdef定义的函数没有Python接口。

  可以发现,我们只是在原来的代码中添加了类型声明,并没有修改多少代码,但是运行速度会得到极大提升。

  由于Cython代码能编译成C语言,因此可以直接与第三方数据处理库交互,例如Numpy,从而提升程序性能。在Cython中,我们可以快速访问Numpy数组。此外,我们之前在Python中操作Numpy的语法在Cython中,仍然可以使用。

  但是如果你想将Cython与Numpy绑定起来,则需要用Cython的语法修改代码。例如,你可以使用 cimport 在代码编译时查看库中的C代码结构。如果你已经安装了NumPy,可以在代码中使用cimport numPy导入Numpy。

  NumPy等Python包,将C语言库封装在了Python接口中,使得调用更加方便。然而,在Python和C之间来回切换会影响代码的运行速度。Cython可以直接调用C语言库(还支持c++库),省去了来回切换花费的时间。

  如果在代码中用到了Python对象,你可以用Python语言中的方法进行内存管理和垃圾回收。也可以使用malloc/free来对C数据结构进行内存管理,但是最后要手动清除。

  Cython通过装饰器和编译器指令(例如@boundscheck(False)),来自动检查C代码运行时的问题,例如越界访问数组就会报错。因此,在默认情况下,Cython生成的C代码要比手写的C代码安全得多,尽管可能速度会慢一些。

  如果你想提升运行速度,就可以禁用自动检查功能,针对整个模块或者具体函数都可以。Cython还可以直接访问内存中的Python数据。

  Python的全局解释器锁(Global Interpreter Lock,简称GIL),它的主要功能是:在解释器中执行的每一个线程,都会先锁住自己,以阻止别的线程执行。但是许多开发者都认为GIL影响了Python程序的性能,尤其是在多核系统上。

  如果你的Python程序中有一段代码没有使用Python对象,可以考虑使用 with nogil ,来释放GIL。这样在这段代码执行期间,解释器就可以运行其他线程的代码了。

  Python有一种类型提示语法,主要用于对代码进行检查。Cython中有可用于代码修饰的自定义语法,但在Cython的最新版本中,你也可以使用Python类型提示语法。这种模式被称为“纯python模式”。我在另一篇文章中介绍了如何使用“纯python模式”[1] 。

  Cython无法将一个漏洞百出的Python代码转换为性能优越的C代码。我们必须了解清楚Cython的局限性:

  Python语法有许多数据结构:字符串、列表、元组、字典等,使用起来很方便,并且自带内存管理机制。但相比纯C语言,速度要慢很多。我们可以使用C语言的变量和数据结构,来提升性能。

  如果你在Cython中,用cdef关键字定义了一个纯C语言编写的函数,那么它的运行速度将与C一样快。但是如果该函数调用了Python代码,那么就会影响性能。

  幸运的是,在Cython中我们可以通过源代告发现上述问题,该报告会直观地显示哪部分是纯C,哪部分与Python交互。一般而言,与Python的交互就越少,性能就越好。

  下图是一个Cython程序的源代告。白域是纯C代码,黄域表示与Python交互的代码。一个性能优越的Cython程序中黄色部分比较少。

  Cython自带性能检测工具,例如cProfile[2] ,我们可以使用该工具分析代码性能。

  Cython也不是万能的,它并不能保证一定可以提高代码运行速度。通常情况下,Python代码和Cpython代码的交互越少,程序的性能越好。例如,如果你需要在Cython中处理一组对象,那么不要在Python中遍历它,然后每一步都去调用一个Cython函数。正确的做法是将整个集合传递给Cython模块,在Cython中进行处理。这一技巧在管理数据库时可以使用。

  我们使用Python是因为它语法简洁,开发效率高,但是Python程序的运行速度比较慢。通过使用Cython,可以有效地提升程序运行速度。

  主页君日常还会在个人微信分享Python相关工具、资源和精选技术文章,不定期分享一些有意思的活动、岗位内推以及如何用技术做业余项目