浅谈Python程序与C++程序的联合使用


Posted in Python onApril 07, 2015

作为Python程序员,应该能够正视Python的优点与缺点。众所周之,Python的运行速度是很慢的,特别是大数据量的运算时,Python会慢得让人难以忍受。对于这种情况,“专业”的解决方案是用上numpy或者opencl。不过有时候为了一点小功能用上这种重型的解决方案很不划算,或者有时候想要实现的操作在numpy里面没有,需要我们自己用C语言来编写。总之,我们使用Python与C++的混合编程能够加快程序热点的运算速度。

首先要提醒大家注意的是,在考虑联合编程之前一定要找到程序运行的热点。简单一点地,使用标准库的profile或者cProfile模块找到最消耗CPU的位置,如果这个位置只简单的消耗IO时间,通常换成C++程序的意义也不会很大,此时做联合编程可能是事倍功半,起不到多大的效果。

还有些情况,Python程序员们想要使用操作系统或者外部模块提供的函数。这些模块一般是为C/C++程序员提供的。这时候也是Python与C++联合编程的用武之地。

Python语言可以说是最好的胶水语言。仅就与C++联合编程这个问题来讲,依使用难度与功能来排列,Python社区提供了以下几种解决方案:

1.使用标准库ctypes直接调用C/C++编写的动态链接库。这是最简单易用的方案。C/C++程序员使用自己的丰富的经验,把预定的功能实现为动态链接库。而Python程序员只要知道这些动态链接库函数的名称、参数类型与返回值类型就能简单地调用它。当你传入参数时,ctypes模块会自动地把Python的对象成为C/C++所对应的参数类型。比如以下调用Windows的API:

#定义参数类型与函数名称
  from ctypes.wintypes import UINT, DWORD
  GetLastInputInfo = ctypes.windll.user32.GetLastInputInfo
  class LASTINPUTINFO(ctypes.Structure):
    _fields_ = [("cbSize", UINT),
         ("dwTime", DWORD)]

  #开始调用DLL导出的函数
  def getLastInputTime_nt():
    info = LASTINPUTINFO()
    info.cbSize = ctypes.sizeof(info)
    info.dwTime = 0
    if not GetLastInputInfo(ctypes.byref(info)):
      raise WindowsError("")
    return info.dwTime

    在这里展示了如何构造Windows的API所需要的结构体,如何填充结构体并分析返回值。

    ctypes还能将Python函数提供给C/C++代码作为回调函数。

    与其它解决方案相比。ctypes不需要程序员熟悉C/C++语言,不需要安装一个C/C++的编译器,它通过操作系统的接口直接操作C/C++代码。而且ctypes是标准库的一部分,只要安装了Python就可以直接使用。这几个原因使得它深受Python程序员的喜爱。

    而它的劣势呢。首先,ctypes不能简单调用C++程序,因为C++在编译的时候使用了name mangling这个技术来实现函数的重载。C++会自动地为类的成员函数加上类名前缀。所以,C++程序员需要以C语言的调用约定来提供接口,没有类,没有重载函数,没有模板,没有C++异常。不能直接调用现有的C++代码可能是这个方案最大的缺点。

    另外,对于list, set之类的数据类型,ctypes不能识别并自动地在Python与C/C++数据类型之间转换。C/C++部分不能识别Python数据类型,这时候只能用Python语言来编写转换代码。如果数据量较大,或者调用很频繁,转换代码反而会浪费很多的资源。这或许是ctypes的另一个劣势之一了。

2.如果你使用的是Jython或者IronPython的话,它们也提供了类似于ctypes之类的模块,能够直接访问Java或者.Net语言编写的模块。其优势与劣势大致与ctypes相似。因为其使用范围有限,这里不再详述。

3.使用Cython语言,一种类似于Python语言的一种新型语言编写预定功能的代码,然后将这些代码转换成为C语言编译成为Python语言可以直接调用的二进制模块。Cython语言是融合Python语言与C语言的一种新型语言。它本身能够理解Python语言的语法,然后在其基础上增加了某些C语言的语法,以便更精细地控制数据类型与指针。基本兼容Python语法是这个解决方案最大的特点。很多时候,Python程序员只要在旧的代码中简单地声明一下代码中所使用的参数、变量的类型,就能把立即为旧的Python程序提速。

    Cython提供了一个名为pyximporter的工具,能够在安装了C/C++编译器的计算机上面为简单的Cython程序直接生成相应的Python模块。这使得Cython的使用与普通的Python程序一样简单。比如下面这段代码,直接保存为myhello.pyx即可被调用。

#myhello.pyx
  def sayHelloTenTimes():
    cdef int i #只要简单地为变量标识类型即可加速循环。
    for i in range(0, 10):
      print("hello, world!")

  $ python
  >>> import pyximport; pyximport.install()
  >>> import myhello
  >>> myhello.sayHelloTenTimes()

    由此可见,Cython非常容易使用。而且不仅能够处理C语言的模块,还能处理C++的模块——虽然没有直接支持虚函数之类的完整C++特性。因为它不直接使用C/C++语法,而是另外设计比C/C++更简洁优雅的新型语法,因此,对于不熟悉C/C++的程序员来说有很大的吸引力。相比ctypes来说,因为参数类型转换更加智能与高效,所以通常能够提升更多的效率。

    劣势呢,所谓用Python程序员所熟练的语法来编写高速的运算代码,乍一听相当地有吸引力。但是如果想要更深入地控制内存与数据结构时,程序员可能会发现,现在他不得不熟练地掌握C/C++语言,然后用Cython的语法写出来。以程序员们懒惰的性格,这反而是件难以忍受的事件。这或许是Cython本身并不大流行的主要原因吧。

4.使用boost.python。有意思的是,与ctypes/Cython形成鲜明的对比,boost.python倾向于让C++程序员拥有更熟悉的编程环境。它让C++程序员使用他所熟悉的C++语法直接控制Python的数据结构,调用Python的解释器。它没有像Cython那样发明新的语法,而是直接使用C++的语法,编写供Python使用的接口。与Cython同样的道理,它的效率优胜于ctypes。

    与Cython/SWIG/SIP等方案相比,程序员只需要学习C/C++与Python两种语言。另外,与本文提到的几种解决方案相比,它非常适合在主要由C++编写的程序中控制Python代码。不仅功能更强大、效率还更高。如此神奇的解决方案会有什么劣势呢?某些人可能不同意吧,老鱼一听说它依赖于boost就蔫了,感觉编译与学习庞大又奇怪的boost非常浪费生命。

5.使用SWIG或者SIP,通过编写一个接口文件,使用类似于C/C++语法——声明函数、类型的信息,然后使用特殊的工具为C/C++的代码生成Python的接口代码。这些接口代码能够在Python与C/C++之间的数据结构转换。最终编译这些接口代码,成为Python的二进制模块。SWIG与SIP的接口文件与C/C++的头文件非常相似。

    这两种工具差不多,因为。本质上,他们都与Cython类似,都使用了中间语言来生成转换代码。但SWIG/SIP能够在他们的接口文件中嵌入C/C++,能够让程序员仔细地调节数据类型的转换过程。在使用上,它比Cython的层次更低,更接近于Python本身提供的API。

    SWIG能够为多种脚本语言生成转换代码。而SIP则专门针对Python与C++。此外,SIP本身是作为PyQt的专门工具来开发的,因此它能够理解Qt的signal/slot。从应用项目上来看,SWIG似乎会更广泛一点。而SIP,目前所见的项目基本都与PyQt相关。据说SWIG对于C++的支持不好,不知道有没有人来说一下呢。相比之下,SIP对于C++的支持非常完善,诸如虚函数、protected member function、模版、析构函数、异常等特性都得到良好的支持。而且SIP支持Python的GIL,还拥有一个使用Python编写的编译系统。可能会更方便一点。

    然而这种方案毕竟要学习一种新的语言,所以从表面上来看不如Cython和boost.python讨喜。当程序员想要仔细地调节类型转换代码的时候,需要学习SWIG/SIP的内部机制,被限定使用特殊的变量名。这使得这种方案的学习曲线相对较高。

6.直接使用Python的API,可以称之为最终解决方案。Cython, SWIG, SIP的接口文件转换后所生成的C/C++代码实际上都使用Python的API。与其它方案相比,这种方案相当地繁复,必须为每次函数调用编写数据转换代码,还要操心Python对象的引用计数。我觉得这种方案一无是处,这时就不再多讲了。其它的工具pybindgen不知道什么情况。有兴趣的话可以看看。

好了。题外话一句吧,我一直觉得ctypes与xmlrpc并列Python语言的两大神器,最能体现Python的生产效率。

希望本文在大家选择一种技术路线时能提供一点点帮助。

Python 相关文章推荐
Python检测网站链接是否已存在
Apr 07 Python
python实现按行切分文本文件的方法
Apr 18 Python
浅谈numpy中linspace的用法 (等差数列创建函数)
Jun 07 Python
python实现校园网自动登录的示例讲解
Apr 22 Python
Django中使用Celery的教程详解
Aug 24 Python
详解Numpy中的广播原则/机制
Sep 20 Python
对python中的高效迭代器函数详解
Oct 18 Python
pygame游戏之旅 添加游戏暂停功能
Nov 21 Python
基于Python第三方插件实现西游记章节标注汉语拼音的方法
May 22 Python
python爬虫分布式获取数据的实例方法
Nov 26 Python
Python 如何实现文件自动去重
Jun 02 Python
Python实现将多张图片合成MP4视频并加入背景音乐
Apr 28 Python
浅要分析Python程序与C程序的结合使用
Apr 07 #Python
python实现根据用户输入从电影网站获取影片信息的方法
Apr 07 #Python
python中列表元素连接方法join用法实例
Apr 07 #Python
简单介绍Python中的filter和lambda函数的使用
Apr 07 #Python
解析Python中的变量、引用、拷贝和作用域的问题
Apr 07 #Python
在Python中利用Pandas库处理大数据的简单介绍
Apr 07 #Python
详解Python中的join()函数的用法
Apr 07 #Python
You might like
apache和php之间协同工作的配置经验分享
2013/04/08 PHP
php实现简单的上传进度条
2015/11/17 PHP
PHP给前端返回一个JSON对象的实例讲解
2018/05/31 PHP
PHP addcslashes()函数讲解
2019/02/03 PHP
根据鼠标的位置动态的控制层的位置
2009/11/24 Javascript
javascript 动态生成私有变量访问器
2009/12/06 Javascript
面向对象的Javascript之二(接口实现介绍)
2012/01/27 Javascript
为EasyUI的Tab标签添加右键菜单的方法
2012/07/14 Javascript
JavaScript自动设置IFrame高度的小例子
2013/06/08 Javascript
Jquery实现图片放大镜效果的思路及代码(自写)
2013/10/18 Javascript
将数字转换成大写的人民币表达式的js函数
2014/09/21 Javascript
深入分析Javascript事件代理
2016/01/30 Javascript
简单总结JavaScript中的String字符串类型
2016/05/26 Javascript
JavaScript结合Bootstrap仿微信后台多图文界面管理
2016/07/22 Javascript
基于JavaScript实现无限加载瀑布流
2017/07/21 Javascript
基于jquery实现五星好评
2017/11/18 jQuery
AngularJS模态框模板ngDialog的使用详解
2018/05/11 Javascript
基于React Native 0.52实现轮播图效果
2020/08/25 Javascript
微信小程序实现的五星评价功能示例
2019/04/25 Javascript
原生javascript制作贪吃蛇小游戏的方法分析
2020/02/26 Javascript
OpenLayers3实现地图显示功能
2020/09/25 Javascript
Python实现快速排序算法及去重的快速排序的简单示例
2016/06/26 Python
pandas.DataFrame选取/排除特定行的方法
2018/07/03 Python
Python SQL查询并生成json文件操作示例
2018/08/17 Python
Python使用numpy产生正态分布随机数的向量或矩阵操作示例
2018/08/22 Python
在python 不同时区之间的差值与转换方法
2019/01/14 Python
python自动结束mysql慢查询会话的实例代码
2019/10/27 Python
python SVD压缩图像的实现代码
2019/11/05 Python
奥地利网上现代灯具和灯饰店:Lampenwelt.at
2018/01/29 全球购物
Haggar官网:美国男装品牌
2020/02/16 全球购物
预备党员公开承诺书
2014/05/28 职场文书
2014年个人委托书范本
2014/10/13 职场文书
2015清明节祭奠英烈寄语大全
2015/03/04 职场文书
2015年秋季小学开学典礼主持词
2015/07/16 职场文书
HTML+CSS实现导航条下拉菜单的示例代码
2021/08/02 HTML / CSS
Nginx 常用配置
2022/05/15 Servers