python c语言拓展,初用C/C++扩展Python,提高性能

前段时间写了两篇文章介绍如何提高Python的运行效率,一篇是从python语言本身的角度去介绍的,另一篇是从解释器角度(利用PyPy),有兴趣的可以找着看看。从另外一个角度来介绍如何提高python运行效率,那就是利用c/c++来扩展python提高性能。我们知道python官方网站上下载的python解释器源码是用c语言编写的,所以,也可以利用c/c++来扩展它,以获得较优的执行性能。Python提供了API接口,是我们很方便的能进行扩展,所有这些API都包含在Python.h的头文件里,在编写c代码时引入该头文件即可。下面来讲讲我初用c/c++扩展python的经历吧。申明所有系统Ubuntu 16.04 LTS。

(一)Ubuntu下如何编译.c文件

说实话,进行这部分学习时,第一困扰我的问题就是不知道怎么在Ubuntu下编译.c文件,以前在大学的时候,用的是Windows系统,安装了visual studio 2010,代码编写,编译都很方便。没有在Ubuntu下用c语言开发,所以这部分知道还不太了解,好在网上资料非常丰富,很快就能找到相应的资料。其实Ubuntu系统自带c语言编译器,那就是gcc。不信你可以打开终端,输入命名:gcc --version,显示如下信息:

dcd1cdec1e7f505f77c42c9e46591de3.png

如果没有的话,你也可自己安装,安装命令如下:sudo apt-get install gcc

建议还要安装一个build-essentiall包,作用是提供编译程序必须软件包的列表信息,也就是说 编译程序有了这个软件包它才知道 头文件在哪 ,才知道库函数在哪,还会下载依赖的软件包 , 最后才组成一个开发环境。

好了,现在写个简单的.c文件吧。

打开终端,输入命令vim hello.c,当然,你也可以用gedit来编辑代码,只要文件扩展名不搞错了就行。代码如下:#include

int main()

{

int i;

for(i=0;i<3;i++)

printf("hello,gcc!\n");

}

然后保存文件并退出。编译成可执行文件,命令如下:gcc hello.c -o hello

然后输入./hello执行该文件。效果如下图。

639cfd7e6fee44247e7ed1fb9646aa07.png

这里只是简单的讲了一下linux的gcc的使用方法,关于它更多的用法可以在网上查资料,在此不做过多介绍。

(二)使用C/C++扩展Python

1.先利用Python提供的接口,编写一个汉语一定功能.c文件,比如判断一个数是不是素数。在这里文件名为test.c,使用c语言实现后再使用Python进行包装。代码如下:#include

static PyObject *pr_isprime(PyObject *self,PyObject *args){

int n,num;

if(!PyArg_ParseTuple(args,"i",&num))

return NULL;

if(num<1){

return Py_BuildValue("i",0);

}

n=num-1;

while(n>1){

if(num%n==0)

return Py_BuildValue("i",0);

n--;

}

return Py_BuildValue("i",1);

}

static PyMethodDef PrMethods[]={

{"isPrime",pr_isprime,METH_VARARGS,"check if an input numbe is prime or not."},

{NULL,NULL,0,NULL}

};

void initpr(void){

(void) Py_InitModule("pr",PrMethods);

}

PyObject是Python对象机制的基础,所有得对象都拥有一些相同的内容,而这些内容在PyObject中定义。

上面的代码包括三部分:

一是导出函数,C模块对外暴露的接口函数pr_isprime,带有self和args两个参数,其中参数args中包含了python解释器要传递给c函数的所有参数,通常使用PyArg_ParseTuple()来获得这些参数。

二是初始化函数,以便python解释器能够对模块进行初始化,初始化时要以init开头,如initx。

第三是方法列表,提供给外部的python程序使用的一个C模块函数名称映射表PrMethods。它是一个PyMethodDef结构体,其中成员依次表示方法名、导出函数、参数传递方式和方法描述。

参数传递方式一般设置为METH_VARARGS,如果想传递关键字参数,你可以让它与MRTH_KEYWORDS进行或运算;如果不想接受任何参数,可以用METH_NOARGS,该结构体必须以{NULL,NULL,0,NULL}所表示的一条空记录结尾。

至于为什么这样做,可能与Python.h里面的函数有关,该部分我还未仔细研究,所以这篇文章也叫题目用了“初用”,如果有以后弄清楚了,我会“再用”,与大家分享学习经验。

2.编写setup.py脚本

代码如下:rom distutils.core import setup,Extension

module = Extension('pr',sources=['test.c'])

setup(name='Pr test',version = '1.0',ext_modules=[module])

distutils包是可以用来在Python环境中构建和安装额外的模块。新的模块可以是纯Python的,也可以是用C/C++写的扩展模块,或者可以是Python包,包中包含了由C和Python编写的模块。在此不做过多介绍,有兴趣可以在网上找资料了解下。

3.使用python setup.py build进行编译,系统会自动在当前目录生成一个build子目录,里面包含一个.so文件、.o文件。看看我的操作结果吧

620ba932621120498615c7c6ef75da7e.png

执行python setup.py build命令后,系统确实在当前目录生成了一个build子目录,并且有一个.so文件、.o文件。

4.将生成的.so文件复制到/usr/local/lib/python2.7/dist-packages下,或者将.so文件所在目录路径添加到sys.path中,然后就可以使用该C扩展模块了。如下图:

c179408944a0a5e85346af2811be7f29.png

(三)简单测试下性能

代码如下:#coding=utf-8

import pr

import datetime

def is_prime(n):

if n <= 1:#

return False

else:

a = n - 1

while(a > 1):

if n % a == 0:

return True

a = a - 1

return False

t1 = datetime.datetime.now()

is_prime(8888888)

t2 = datetime.datetime.now()

pr.isPrime(8888888)

t3 = datetime.datetime.now()

print t3 - t2,t2 - t1

其中is_prime()是用python写的判断一个正数是不是素数。它的循环算法与pr模块里面的isPrime()是一样的,但是效率却有很大差异,看看下图运行结果。

b139db3cba6c31d84a3bc66f855d6080.png

很显然用c扩展的那个包里的函数计算速度快而且是块近20倍,数值越大,速度上的差距越大,有兴趣的话,可以动手试下。当然这只是一个定性分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值