Python和C/C++交互的几种方法总结


Posted in Python onMay 11, 2017

前言

python作为一门脚本语言,其好处是语法简单,很多东西都已经封装好了,直接拿过来用就行,所以实现同样一个功能,用Python写要比用C/C++代码量会少得多。但是优点也必然也伴随着缺点(这是肯定的,不然还要其他语言干嘛),python最被人诟病的一个地方可能就是其运行速度了。这这是大部分脚本语言共同面对的问题,因为没有编译过程,直接逐行执行,所以要慢了一大截。所以在一些对速度要求很高的场合,一般都是使用C/C++这种编译型语言来写。但是很多时候,我们既想使用python的简介优美,又不想损失太多的性能,这个时候有没有办法将python与C/C++结合到一起呢?这样在性能与速度要求不高的地方,可以用pyhton写,而关键的运算部分用C/C++写,这样就太好了。python在做科学计算或者数据分析时,这是一个非常普遍的需求。要想实现这个功能,python为我们提供了不止一种解决办法。

下面我就逐一给大家介绍。

一、Cython 混合python与C

官方网址:http://docs.cython.org/en/latest/src/quickstart/overview.html。首先来看看cython的官方介绍吧。

[Cython] is a programming language that makes writing C extensions for the Python language as easy as Python itself. It aims to become a superset of the [Python]language which gives it high-level,  object-oriented, functional, and dynamic programming. Its main feature on top of these is support for optional static type declarations as part of the language. The source code gets translated into optimized C/C++ code and compiled as Python extension modules. This allows for both very fast program execution and tight integration with external C libraries, while keeping up the high programmer productivity for which the Python language is well known.

简单来说,cython就是一个内置了c数据类型的python,它是一个python的超集,兼容几乎所有的纯python代码,但是又可以使用c的数据类型。这样就可以同时使用c库,又不失python的优雅。

好了,不讲太多废话,直接来看cython如何使用吧。这里的介绍大部分来自官网,由于cython涉及到的东西还比较多,所以这里只是简单的入门介绍,详细的信息请移步英文官网。

使用cython有两种方式:第一个是编译生成Python扩展文件(有点类似于dll,即动态链接库),可以直接import使用。第二个是使用jupyter notebook或sage notebook 内联 cython代码。

先看第一种。还是举最经典的hello world的例子吧。新建一个hello.pyx文件,定义一个hello函数如下:

def hello(name):
 print("Hello %s." % name)

然后,我们来写一个setup.py 文件(写python扩展几乎都要写setup.py文件,我之前也简单介绍过怎么写)如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/5/8 9:09
# @Author : Lyrichu
# @Email : 919987476@qq.com
# @File : setup.py
'''
@Description: setup.py for hello.pyx
'''
from Cython.Build import cythonize
from distutils.core import setup

# 编写setup函数
setup(
 name = "Hello",
 ext_modules = cythonize("hello.pyx")
)

其中 ext_modules 里面写你要 编译的.pyx文件名字。OK,所有工作都完成了。接下来,进入cmd,切换到setup.py 所在的文件,然后执行命令: python setup.py build_ext --inplace 就会编译生成一个build 文件夹以及一个.pyd文件了,这个pyd文件就是python的动态扩展库,--inplace 的意思是在当前文件目录下生成.pyd文件,不加这一句就会在build文件夹中生成。

截图如下:

Python和C/C++交互的几种方法总结

图 1

可以看出,除了生成了一个pyd文件之外,还生成了一个.c文件。test.py是我们用来测试的文件,在里面写如下内容:

from hello import hello
hello("lyric")

从hello 模块导入 hello函数,然后直接调用就可以了。结果输出 Hello lyric.

再来看如何 在 jupyter notebook中使用cython。如果你装过ipython,一个升级版的python交互式环境,你应该听过 ipyhton notebook的大名,现在它升级了,改名叫jupyter notebook 了。简单来说,这个就是一个可以在网页环境下交互式使用python的工具,不仅可以实时看到计算结果,还可以直接展示表格,图片等,功能还是非常强大的。首先你得安装jupyter notebook.我印象中安装了ipython之后应该就会带了jupyter了。如果没有,可以直接 pip install jupyter .然后输入命令 jupyter notebook 就会在浏览器中打开jupyter了。

如下图2 所示:

Python和C/C++交互的几种方法总结

图 2

点击右上角的new按钮,可以选择新建一个文本文件或者文件夹,markdown或者python文件,这里我们选择新建一个pyhton 文件,然后就会转到一个新的窗口了,如下图3:

Python和C/C++交互的几种方法总结

图 3

In[]:和ipython一样,就代表着我们要输入代码的地方,输入代码之后,点击向右的三角形符号,就会执行代码了。

首先输入 %load_ext cython ,然后执行,%开头的语句是jupyter的魔法命令,%是行命令,%%是单元命令,具体不多说,有空给大家专门介绍一下notebook的使用。

接下来输入:

%%cython
 cdef int a = 0
 for i in range(10):
  a += i
 print(a)

%%cython 表明将cython内嵌到jupyter,cdef 是cython的关键字,用于定义c类型,这里将a定义为c中的int类型,并且初始化为0.

然后后面的循环就是累加0到9的意思,最后输出45.

另外,我们如果想分析代码 的执行情况,可以输入 %%cython --annotate 命令,这样就可以输出结果的同时,也输出 详细的代码执行情况报告了。

截图如图4 所示:

Python和C/C++交互的几种方法总结

图 4

jupyter notebook 可以内嵌cython,不用我们手写setup.py 文件,省去了编译的过程,方便了cython的使用,所以不是正式做项目,只是写一写小东西用jupyter+cython还是非常方便的。

前面提到了 cdef,再举一个稍微复杂点的例子吧。还是引用官网的例子,写一个算积分的函数.新建 integrate.pyx 文件,写入如下内容:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/5/8 9:26
# @Author : Lyrichu
# @Email : 919987476@qq.com
# @File : integrate.py
'''
@Description: 积分运算,使用 cython cdef 关键字
'''
def f(double x):
 return x**2 - x

def integrate_f(double a,double b,int N):
 cdef int i
 cdef double s,dx
 s = 0
 dx = (b-a)/N
 for i in range(N):
  s += f(a + i*dx)*dx
 return s # 返回定积分

这段代码应该也是比较好理解的, f()函数是被积函数,a,b是积分的上下限,N是分割小矩形的个数,注意这里将 变量i,s,dx全部都用cdef 声明为c类型了,一般来说,在需要密集计算的地方比如循环或者复杂运算,可以将对应的变量声明为c类型,可以加快运行速度。

然后和上面一样编写 setup.py ,就是把 pyx的文件名改一下,代码我就不贴了。然后python setup.py build_ext --inplace 执行。得到pyd文件,编写测试文件test.py如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/5/8 9:35
# @Author : Lyrichu
# @Email : 919987476@qq.com
# @File : test.py
'''
@Description: 测试使用cython 混合c与python的integrate 函数与纯python写的integrate函数速度上的差异
'''
from integrate import integrate_f
import time

a = 1 # 积分区间下界
b = 2 # 积分区间上界
N = 10000 # 划分区间个数

# 使用纯python代码写的integrate函数
def py_f(x):
 return x**2 - x

def py_integrate_f(a,b,N):
 dx = (b-a)/N
 s = 0
 for i in range(N):
  s += py_f(a + i*dx)*dx
 return s

start_time1 = time.time()
integrate_f_res = integrate_f(a,b,N)
print("integrate_f_res = %s" % integrate_f_res)
end_time1 = time.time()
print(u"cython 版本计算耗时:%.8f" % (end_time1 - start_time1))

start_time2 = time.time()
py_integrate_f_res = py_integrate_f(a,b,N)
print("py_integrate_f_res = %s" % py_integrate_f_res)
end_time2 = time.time()
print(u"python 版本计算耗时:%.8f" % (end_time2 - start_time2))

上面的代码,我们重新使用python写了一个积分函数py_integrate_f,与pyd中的integrate_f 函数进行运算对比,结果如下(图5):

Python和C/C++交互的几种方法总结

图5

可以看出,使用了cython的版本比纯Python的版本大概快了4、5倍的样子,而这仅仅是将几个变量改为c类型的结果,可见,cython确实可以方便地对python与c进行混合,获得速度上的提升,又不失去Python的简洁优美。

最后再来说下cython 如何调用c libraries. C 语言 stdlib 库有一个 atoi函数,可以将字符串转化为整数,math库有一个sin函数,我们就以这两个函数为例。新建 calling_c.pyx 文件,文件内容如下:

from libc.stdlib cimport atoi
from libc.math cimport sin

def parse_char_to_int(char * s):
 assert s is not NULL,"byte string value is NULL"
 return atoi(s)

def f_sin_squared(double x):
 return sin(x*x)

前两行导入了C语言中的函数,然后我们自定义了两个函数,parse_char_to_int 可以将字符串转换为整数,f_sin_squared 计算 x平方的sin函数值。写 setup.py 文件,和之前差不多,但是要注意的是,在unix系统下,math库默认是不链接的,所以需要指明其位置,那么在unix系统下,setup.py 文件的内容就需要增加Extension 一项,如下:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

ext_modules=[
 Extension("calling_c",
    sources=["calling_c.pyx"],
    libraries=["m"] # Unix-like specific
 )
]

setup(
 name = "Calling_c",
 ext_modules = cythonize(ext_modules)
)

然后直接编即可。test.py文件如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/5/8 12:21
# @Author : Lyrichu
# @Email : 919987476@qq.com
# @File : test.py
'''
@Description: test file
'''
from calling_c import f_sin_squared,parse_char_to_int
str = "012"
str_b = bytes(str,encoding='utf-8')
n = parse_char_to_int(str_b)
print("n = %d" %n)
from math import pi,sqrt
x = sqrt(pi/2)
res = f_sin_squared(x)
print("sin(pi/2)=%f" % res)

需要注意的是,Python字符串不能直接传入 parse_char_to_int 函数,需要将其转换为 bytes 类型再传入。运行结果为:

n = 12
sin(pi/2)=1.000000

如果不想通过libc导入c语言模块,cython也允许我们自己声明c函数原型来导入,一个例子如下:

# 自己声明c函数原型
cdef extern from "math.h":
 cpdef double cos(double x)

def f_cos(double x):
 return cos(x)

使用了 extern 关键字。

每次都编写setup.py 文件,然后编译,略显麻烦。cython还提供了一种更简单的方法:pyximport。通过导入pyximport(安装cython时会自动安装),在没有引入额外的c库的情况下,可以直接调用pyx中的函数,更为直接与方便。以前面的hello 模块为例,编写好hello.py文件之后,编写一个pyximport_test.py 文件,文件内容如下:

import pyximport
pyximport.install()
import hello
hello.hello("lyric")

直接运行就会发现,确实可以正确导入hello模块。

cython的更多内容,请大家自行访问官网查看。

其他python与c/c++ 混合编程的方式主要还有 使用 ctypes,cffi模块以及swig。本来想一起写的,想想还是分开写吧,不然太长了。后续会陆续更新,敬请关注。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python 自动提交和抓取网页
Jul 13 Python
python创建临时文件夹的方法
Jul 06 Python
python机器学习理论与实战(一)K近邻法
Jan 28 Python
简单实现Python爬取网络图片
Apr 01 Python
Pycharm配置远程调试的方法步骤
Dec 17 Python
如何使用Python进行OCR识别图片中的文字
Apr 01 Python
python3实现网页版raspberry pi(树莓派)小车控制
Feb 12 Python
Keras之自定义损失(loss)函数用法说明
Jun 10 Python
Python描述数据结构学习之哈夫曼树篇
Sep 07 Python
Ubuntu 20.04安装Pycharm2020.2及锁定到任务栏的问题(小白级操作)
Oct 29 Python
python3字符串输出常见面试题总结
Dec 01 Python
python安装及变量名介绍详解
Dec 12 Python
Python常用时间操作总结【取得当前时间、时间函数、应用等】
May 11 #Python
详解Python读取配置文件模块ConfigParser
May 11 #Python
python如何获取服务器硬件信息
May 11 #Python
浅谈Python基础之I/O模型
May 11 #Python
老生常谈Python进阶之装饰器
May 11 #Python
python 第三方库的安装及pip的使用详解
May 11 #Python
插入排序_Python与PHP的实现版(推荐)
May 11 #Python
You might like
php include,include_once,require,require_once
2008/09/05 PHP
20个2014年最优秀的PHP框架回顾
2014/10/22 PHP
jQuery向下滚动即时加载内容实现的瀑布流效果
2016/01/07 PHP
PHP内核学习教程之php opcode内核实现
2016/01/27 PHP
总结PHP中数值计算的注意事项
2016/08/14 PHP
JQuyer $.post 与 $.ajax 访问WCF ajax service 时的问题需要注意的地方
2011/09/20 Javascript
13 个JavaScript 性能提升技巧分享
2012/07/26 Javascript
浅谈javascript 函数属性和方法
2015/01/21 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
轻松掌握JavaScript装饰者模式
2016/08/27 Javascript
微信小程序 实例应用(记账)详解
2016/09/28 Javascript
Angular ui.bootstrap.pagination分页
2017/01/20 Javascript
Three.js的使用及绘制基础3D图形详解
2017/04/27 Javascript
将input框中输入内容显示在相应的div中【三种方法可选】
2017/05/08 Javascript
JavaScript求一个数组中重复出现次数最多的元素及其下标位置示例
2018/07/23 Javascript
vue拖拽组件使用方法详解
2018/12/01 Javascript
基于three.js实现的3D粒子动效实例代码
2019/04/09 Javascript
Vue Cli3 打包配置并自动忽略console.log语句的方法
2020/04/23 Javascript
在nodejs中创建child process的方法
2021/01/26 NodeJs
JavaScript实现点击切换功能
2021/01/27 Javascript
[01:02:54]完美世界DOTA2联赛PWL S2 FTD vs GXR 第一场 11.22
2020/11/26 DOTA
Python字符串切片操作知识详解
2016/03/28 Python
PyCharm安装第三方库如Requests的图文教程
2018/05/18 Python
在cmder下安装ipython以及环境的搭建
2018/10/19 Python
解决python3读取Python2存储的pickle文件问题
2018/10/25 Python
python基于socket进行端口转发实现后门隐藏的示例
2019/07/25 Python
python实现TCP文件传输
2020/03/20 Python
一款css实现的鼠标经过按钮的特效
2014/09/11 HTML / CSS
怎样从/向数据文件读/写结构
2014/11/23 面试题
医药营销专业个人自荐信
2013/09/29 职场文书
大学优秀班主任事迹材料
2014/05/02 职场文书
水利水电建筑施工应届生求职信
2014/07/04 职场文书
大学毕业典礼演讲稿
2014/09/09 职场文书
2016三八妇女节校园广播稿
2015/12/17 职场文书
社会实践心得体会范文
2016/01/14 职场文书
Vue+Flask实现图片传输功能
2022/04/01 Vue.js