浅谈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生态系统(Python新手入门指导)
Dec 11 Python
python flask 多对多表查询功能
Jun 25 Python
Python基于QRCode实现生成二维码的方法【下载,安装,调用等】
Jul 11 Python
给你选择Python语言实现机器学习算法的三大理由
Nov 15 Python
利用python循环创建多个文件的方法
Oct 25 Python
解决pycharm运行时interpreter为空的问题
Oct 29 Python
Python设计模式之适配器模式原理与用法详解
Jan 15 Python
Python 多个图同时在不同窗口显示的实现方法
Jul 07 Python
在tensorflow中实现去除不足一个batch的数据
Jan 20 Python
Python for循环搭配else常见问题解决
Feb 11 Python
Python selenium爬取微信公众号文章代码详解
Aug 12 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
thinkphp在模型中自动完成session赋值示例代码
2014/09/09 PHP
php通过header发送自定义数据方法
2018/01/18 PHP
thinkPHP中U方法加密传递参数功能示例
2018/05/29 PHP
PHP实现字符串的全排列详解
2019/04/24 PHP
页面定时刷新(1秒刷新一次)
2013/11/22 Javascript
JQuery动画animate的stop方法使用详解
2014/05/09 Javascript
nodejs教程之入门
2014/11/21 NodeJs
Javascript对象Clone实例分析
2015/06/09 Javascript
Jquery幻灯片特效代码分享--鼠标点击按钮时切换(1)
2015/08/15 Javascript
JS从数组中随机取出几个数组元素的方法
2016/08/02 Javascript
js实现一个简单的MVVM框架示例
2018/01/15 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
2019/07/11 jQuery
p5.js临摹旋转爱心
2019/10/23 Javascript
javascript/jquery实现点击触发事件的方法分析
2019/11/11 jQuery
vue打开其他项目页面并传入数据详解
2020/11/25 Vue.js
[19:14]DOTA2 HEROS教学视频教你分分钟做大人-维萨吉
2014/06/24 DOTA
Python 文件操作技巧(File operation) 实例代码分析
2008/08/11 Python
python单元测试unittest实例详解
2015/05/11 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python遍历文件夹 处理json文件的方法
2019/01/22 Python
python整合ffmpeg实现视频文件的批量转换
2019/05/31 Python
PyTorch笔记之scatter()函数的使用
2020/02/12 Python
python解压zip包中文乱码解决方法
2020/11/27 Python
品恩科技软件测试面试题
2014/10/26 面试题
中英文自我评价常用句型
2013/12/19 职场文书
电气工程师岗位职责
2014/01/01 职场文书
最经典的商业地产项目广告词
2014/03/13 职场文书
水毁工程实施方案
2014/04/01 职场文书
《雨点儿》教学反思
2014/04/14 职场文书
化妆品活动策划方案
2014/05/23 职场文书
初中毕业典礼演讲稿
2014/09/09 职场文书
处级干部反四风个人对照检查材料思想汇报
2014/09/27 职场文书
优秀班主任先进事迹材料
2014/12/16 职场文书
高中英语教学反思范文
2016/03/02 职场文书
mapstruct的用法之qualifiedByName示例详解
2022/04/06 Java/Android
Python 避免字典和元组的多重嵌套问题
2022/07/15 Python