浅谈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下singleton模式的实现方法
Jul 16 Python
Python中使用gzip模块压缩文件的简单教程
Apr 08 Python
python实现将html表格转换成CSV文件的方法
Jun 28 Python
Python Sqlite3以字典形式返回查询结果的实现方法
Oct 03 Python
python模块简介之有序字典(OrderedDict)
Dec 01 Python
Python之os操作方法(详解)
Jun 15 Python
详解Matplotlib绘图之属性设置
Aug 23 Python
Python命令行click参数用法解析
Dec 19 Python
pytorch实现focal loss的两种方式小结
Jan 02 Python
Python如何急速下载第三方库详解
Nov 02 Python
Python django框架 web端视频加密的实例详解
Nov 20 Python
python实现scrapy爬虫每天定时抓取数据的示例代码
Jan 27 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
PHP自定义错误处理的方法分析
2018/12/19 PHP
浅谈php://filter的妙用
2019/03/05 PHP
laravel框架查询数据集转为数组的两种方法
2019/10/10 PHP
ajax 文件上传应用简单实现
2009/03/03 Javascript
Prototype源码浅析 String部分(四)之补充
2012/01/16 Javascript
HTML+CSS+JS实现完美兼容各大浏览器的TABLE固定列
2015/04/26 Javascript
Node.js项目中调用JavaScript的EJS模板库的方法
2016/03/11 Javascript
javascript的列表切换【实现代码】
2016/05/03 Javascript
Bootstrap模块dropdown实现下拉框响应
2016/05/22 Javascript
JavaScript实现打开链接页面的方式汇总
2016/06/02 Javascript
Spring shiro + bootstrap + jquery.validate 实现登录、注册功能
2017/06/02 jQuery
全面介绍vue 全家桶和项目实例
2017/12/27 Javascript
微信小程序实现上传图片功能
2018/05/28 Javascript
详解TypeScript+Vue 插件 vue-class-component的使用总结
2019/02/18 Javascript
构建Vue大型应用的10个最佳实践(小结)
2019/11/07 Javascript
Vue事件处理原理及过程详解
2020/03/11 Javascript
Node快速切换版本、版本回退(降级)、版本更新(升级)
2021/01/07 Javascript
Python列表切片操作实例总结
2019/02/19 Python
django-crontab 定时执行任务方法的实现
2019/09/06 Python
python实现斗地主分牌洗牌
2020/06/22 Python
Python 中的函数装饰器和闭包详解
2021/02/06 Python
乐天旅游香港网站:日本饭店预订
2017/11/29 全球购物
Java的for语句中break, continue和return的区别
2013/12/19 面试题
外贸业务员的岗位职责
2013/11/23 职场文书
淘宝活动策划方案
2014/02/06 职场文书
骨干教师培训方案
2014/05/06 职场文书
酒店周年庆活动方案
2014/08/21 职场文书
法律专业大学生职业生涯规划书:向目标一步步迈进
2014/09/22 职场文书
山东省召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
2014年实习期工作总结
2014/11/27 职场文书
2014年体检中心工作总结
2014/12/23 职场文书
幼儿园新学期开学寄语
2015/05/27 职场文书
中国式结婚:司仪主持词(范文)
2019/07/25 职场文书
Nginx stream 配置代理(Nginx TCP/UDP 负载均衡)
2021/11/17 Servers
分享MySQL常用 内核 Debug 几种常见方法
2022/03/17 MySQL
JS前端可扩展的低代码UI框架Sunmao使用详解
2022/07/23 Javascript