简单生活 发表于 2015-1-14 21:16:51

带来一篇Python 代码优化罕见技能

小知识:CentOS其实就是相当于免费版的RedHat,任何人可以自由使用,不需要向RedHat付任何的费用。当然,同样你也得不到任何有偿的技术支持和升级服务。
代码优化可以让步伐运转更快,它是在不改动步伐运转了局的情形下使得步伐的运转效力更高,依据80/20准绳,完成步伐的重构、优化、扩大和文档相干的事变一般必要损耗80%的事情量。优化一般包括两方面的内容:减小代码的体积,进步代码的运转效力。
  改善算法,选择符合的数据布局
  一个优秀的算法可以对功能起到关头感化,因而功能改善的主要点是对算法的改善。在算法的工夫庞大度排序上顺次是:
O(1)->O(lgn)->O(nlgn)->O(n^2)->O(n^3)->O(n^k)->O(k^n)->O(n!)
  因而假如可以在工夫庞大度上对算法举行必定的改善,对功能的进步不问可知。但对详细算法的改善不属于本文会商的局限,读者能够自行参考这方面材料。上面的内容将会合会商数据布局的选择。


[*]字典(dictionary)与列表(list)
  Python字典中利用了hashtable,因而查找操纵的庞大度为O(1),而list实践是个数组,在list中,查找必要遍历全部list,其庞大度为O(n),因而对成员的查找会见等操纵字典要比list更快。
  清单1.代码dict.py

fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t

  上述代码运转也许必要16.09seconds。假如往失落行#list=dict.fromkeys(list,True)的正文,将list转换为字典以后再运转,工夫约莫为8.375seconds,效力也许进步了一半。因而在必要多半据成员举行频仍的查找大概会见的时分,利用dict而不是list是一个较好的选择。


[*]汇合(set)与列表(list)
  set的union,intersection,difference操纵要比list的迭代要快。因而假如触及到求list交集,并集大概差的成绩能够转换为set来操纵。
  清单2.求list的交集:

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t

  上述步伐的运转工夫也许为:
totalruntime:38.4070000648
  清单3.利用set求交集

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):list(set(lista)&set(listb))print"totalruntime:"printtime()-t

  改成set后步伐的运转工夫缩减为8.75,进步了4倍多,运转工夫年夜年夜延长。读者能够自利用用表1其他的操纵举行测试。
  表1.set罕见用法
语法操纵申明set(list1)set(list2)union包括list1和list2一切数据的新汇合set(list1)&set(list2)intersection包括list1和list2中配合元素的新汇合set(list1)-set(list2)difference在list1中呈现但不在list2中呈现的元素的汇合  对轮回的优化
  对轮回的优化所遵守的准绳是只管削减轮回过程当中的盘算量,有多重轮回的只管将内层的盘算提到上一层。上面经由过程实例来对照轮回优化后所带来的功能的进步。步伐清单4中,假如不举行轮回优化,其也许的运转工夫约为132.375。
  清单4.为举行轮回优化前

fromtimeimporttimet=time()lista=listb=foriinrange(1000000):forainrange(len(lista)):forbinrange(len(listb)):x=lista+listbprint"totalruntime:"printtime()-t

  如今举行以下优化,将长度盘算提到轮回外,range用xrange取代,同时将第三层的盘算lista提到轮回的第二层。
  清单5.轮回优化后

fromtimeimporttimet=time()lista=listb=len1=len(lista)len2=len(listb)foriinxrange(1000000):forainxrange(len1):temp=listaforbinxrange(len2):x=temp+listbprint"totalruntime:"printtime()-t

  上述优化后的步伐其运转工夫延长为102.171999931。在清单4中lista被盘算的次数为1000000*10*10,而在优化后的代码中被盘算的次数为1000000*10,盘算次数年夜幅度延长,因而功能有所提拔。
  充实使用Lazyif-evaluation的特征
  python中前提表达式是lazyevaluation的,也就是说假如存在前提表达式ifxandy,在x为false的情形下y表达式的值将不再盘算。因而能够使用该特征在必定水平上进步步伐效力。
  清单6.使用Lazyif-evaluation的特征

fromtimeimporttimet=time()abbreviations=foriinrange(1000000):forwin(Mr.,Hat,is,chasing,the,black,cat,.):ifwinabbreviations:#ifw[-1]==.andwinabbreviations:passprint"totalruntime:"printtime()-t

  在未举行优化之出息序的运转工夫也许为8.84,假如利用正文行取代第一个if,运转的工夫也许为6.17。
  字符串的优化
  python中的字符串工具是不成改动的,因而对任何字符串的操纵如拼接,修正等都将发生一个新的字符串工具,而不是基于原字符串,因而这类延续的copy会在必定水平上影响python的功能。对字符串的优化也是改良功能的一个主要的方面,出格是在处置文本较多的情形下。字符串的优化次要会合在以下几个方面:

[*]在字符串联接的利用只管利用join()而不是+:在代码清单7中利用+举行字符串联接也许必要0.125s,而利用join延长为0.016s。因而在字符的操纵上join比+要快,因而要只管利用join而不是+。
  清单7.利用join而不是+毗连字符串

fromtimeimporttimet=time()s=""list=foriinrange(10000):forsubstrinlist:s+=substrprint"totalruntime:"printtime()-t

  同时要制止:
s=""forxinlist:s+=func(x)
  而是要利用:
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t0

[*]当对字符串可使用正则表达式大概内置函数来处置的时分,选择内置函数。如str.isalpha(),str.isdigit(),str.startswith((x,yz)),str.endswith((x,yz))
[*]对字符举行格局化比间接串连读取要快,因而要利用
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t1
  而制止
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t2
  利用列表剖析(listcomprehension)和天生器表达式(generatorexpression)
  列表剖析要比在轮回中从头构建一个新的list更加高效,因而我们能够使用这一特征来进步运转的效力。

fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t3

  利用列表剖析:
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t4
  上述代码间接运转也许必要17s,而改成利用列表剖析后,运转工夫延长为9.29s。快要进步了一半。天生器表达式则是在2.4中引进的新内容,语法和列表剖析相似,可是在年夜数据量处置时,天生器表达式的上风较为分明,它其实不创立一个列表,只是前往一个天生器,因而效力较高。在上述例子上中代码a=修正为a=(wforwinlist),运转工夫进一步削减,延长约为2.98s。
  其他优化技能

[*]假如必要互换两个变量的值利用a,b=b,a而不是借助两头变量t=a;a=b;b=t;

fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t5


[*]在轮回的时分利用xrange而不是range;利用xrange能够节俭大批的体系内存,由于xrange()在序列中每次挪用只发生一个整数元素。而range()⒅苯臃祷赝暾脑亓斜恚糜谘肥被嵊胁槐匾目Tpython3中xrange不再存在,内里range供应一个能够遍历恣意长度的局限的iterator。
[*]利用部分变量,制止"global"关头字。python会见部分变量会比全局变量要快很多,因此能够使用这一特征提拔功能。
[*]ifdoneisnotNone比语句ifdone!=None更快,读者能够自行考证;
[*]在耗时较多的轮回中,能够把函数的挪用改成内联的体例;
[*]利用级联对照"x<y<z"而不是"x<yandy<z";
[*]while1要比whileTrue更快(固然后者的可读性更好);
[*]buildin函数一般较快,add(a,b)要优于a+b。
  定位步伐功能瓶颈
  对代码优化的条件是必要懂得功能瓶颈在甚么中央,步伐运转的次要工夫是损耗在那里,关于对照庞大的代码能够借助一些工具来定位,python内置了丰厚的功能剖析工具,如profile,cProfile与hotshot等。个中Profiler是python自带的一组步伐,可以形貌步伐运转时分的功能,并供应各类统计匡助用户定位步伐的功能瓶颈。Python尺度模块供应三种profilers:cProfile,profile和hotshot。
  profile的利用十分复杂,只必要在利用之行进行import便可。详细实比方下:
  清单8.利用profile举行功能剖析

fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t6

  步伐的运转了局以下:
  .功能剖析了局

  个中输入每列的详细注释以下:


[*]ncalls:暗示函数挪用的次数;
[*]tottime:暗示指定函数的总的运转工夫,撤除函数中挪用子函数的运转工夫;
[*]percall:(第一个percall)即是tottime/ncalls;
[*]cumtime:暗示该函数及其一切子函数的挪用运转的工夫,即函数入手下手挪用到前往的工夫;
[*]percall:(第二个percall)即函数运转一次的均匀工夫,即是cumtime/ncalls;
[*]filename:lineno(function):每一个函数挪用的详细信息;
  假如必要将输入以日记的情势保留,只必要在挪用的时分到场别的一个参数。如profile.run("profileTest()","testprof")。
  关于profile的分析数据,假如以二进制文件的时分保留了局的时分,能够经由过程pstats模块举行文本报表剖析,它撑持多种情势的报表输入,是文本界面下一个较为有用的工具。利用十分复杂:
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t7
  个中sort_stats()***可以对剖分数据举行排序,能够承受多个排序字段,如sort_stats(name,file)将起首依照函数称号举行排序,然后再依照文件名举行排序。罕见的排序字段有calls(被挪用的次数),time(函数外部运转工夫),cumulative(运转的总工夫)等。别的pstats也供应了下令行交互工具,实行python&ndash;mpstats后能够经由过程help懂得更多利用体例。
  关于年夜型使用步伐,假如可以将功能剖析的了局以图形的体例出现,将会十分有用和直不雅,罕见的可视化工具有Gprof2Dot,visualpytune,KCacheGrind等,读者能够自行查阅相干官网,本文不做具体会商。
  Python功能优化工具
  Python功能优化除改善算法,选用符合的数据布局以外,另有几种关头的手艺,好比将关头python代码部分重写成C扩大模块,大概选用在功能上更加优化的注释器等,这些在本文中统称为优化工具。python有良多自带的优化工具,如Psyco,Pypy,Cython,Pyrex等,这些优化工具半斤八两,本节选择几种举行先容。
  Psyco
  psyco是一个just-in-time的编译器,它可以在不改动源代码的情形下进步必定的功能,Psyco将操纵编译成有点优化的呆板码,其操纵分红三个分歧的级别,有"运转时"、"编译时"和"假造时"变量。并依据必要进步和下降变量的级别。运转时变量只是惯例Python注释器处置的原始字节码和工具布局。一旦Psyco将操纵编译成呆板码,那末编译时变量就会在呆板存放器和可间接会见的内存地位中暗示。同时python能高速缓存已编译的呆板码以备从此重用,如许能节俭一点工夫。但Psyco也有其弱点,其自己运转所占内存较年夜。今朝psyco已不在python2.7中撑持,并且不再供应保护和更新了,对其感乐趣的能够参考http://psyco.sourceforge.net/
  Pypy
  PyPy暗示"用Python完成的Python",但实践上它是利用一个称为RPython的Python子集完成的,可以将Python代码转成C,.NET,Java等言语宁静台的代码。PyPy集成了一种立即(JIT)编译器。和很多编译器,注释器分歧,它不体贴Python代码的词法剖析和语法树。由于它是用Python言语写的,以是它间接使用Python言语的CodeObject.。CodeObject是Python字节码的暗示,也就是说,PyPy间接剖析Python代码所对应的字节码,,这些字节码即不是以字符情势也不是以某种二进制格局保留在文件中,而在Python运转情况中。今朝版本是1.8.撑持分歧的平台安装,windows上安装Pypy必要先下载https://bitbucket.org/pypy/pypy/downloads/pypy-1.8-win32.zip,然后解压到相干的目次,并将解压后的路径增加到情况变量path中便可。在下令交运行pypy,假如呈现以下毛病:"没有找到MSVCR100.dll,因而这个使用步伐未能启动,从头安装使用步伐大概会修复此成绩",则还必要在微软的官网高低载VS2010runtimelibraries办理该成绩。详细地点为http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=5555
  安装乐成后在下令行里运转pypy,输入了局以下:

fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t8

  以清单5的轮回为例子,利用python和pypy分离运转,失掉的运转了局分离以下:
fromtimeimporttimet=time()list=#list=dict.fromkeys(list,True)printlistfilter=[]foriinrange(1000000):forfindin:iffindnotinlist:filter.append(find)print"totalruntime:"printtime()-t9
  可见利用pypy来编译和运转步伐,其效力年夜年夜的进步。
  Cython
  Cython是用python完成的一种言语,能够用来写python扩大,用它写出来的库都能够经由过程import来载进,功能上比python的快。cython里能够载进python扩大(好比importmath),也能够载进c的库的头文件(好比:cdefexternfrom"math.h"),别的也能够用它来写python代码。将关头部分重写成C扩大模块
  LinuxCpython的安装:
  第一步:下载

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t0

  第二步:解压
fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t1
  第三步:安装
fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t2
  安装完成后间接输出cython,假如呈现以下内容则标明安装乐成。

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t3

  其他平台上的安装能够参考文档:http://docs.cython.org/src/quickstart/install.html
  Cython代码与python分歧,必需先编译,编译一样平常必要经由两个阶段,将pyx文件编译为.c文件,再将.c文件编译为.so文件。编译有多种***:


[*]经由过程下令行编译:
  假定有以下测试代码,利用下令行编译为.c文件。

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t4

  在linux上使用gcc编译为.so文件:  

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t5

  利用distutils编译
  创建一个setup.py的剧本:

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t6

  编译完成以后能够导进到python中利用:

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t7

  上面来举行一个复杂的功能对照:
  清单9.Cython测试代码

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t8

  测试了局:

fromtimeimporttimet=time()lista=listb=intersection=[]foriinrange(1000000):forainlista:forbinlistb:ifa==b:intersection.append(a)print"totalruntime:"printtime()-t9

  清单10.Python测试代码

totalruntime:38.40700006480

  从上述对照能够看到利用Cython的速率进步了快要100多倍。
  总结
  本文开端切磋了python罕见的功能优化技能和怎样借助工具来定位和剖析步伐的功能瓶颈,并供应了相干能够举行功能优化的工具或言语,但愿可以更相干职员一些参考。
小知识:Linux是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。

简单生活 发表于 2015-1-17 09:24:23

生成新的unispimsp.ksc。”另外得到回复后如果问题解决,向帮助过你的人发个说明,让他们知道问题是怎样解决的。

若相依 发表于 2015-1-21 07:07:21

对我们学习操作系统有很大的帮助,加深我们对OS的理解。?

愤怒的大鸟 发表于 2015-1-30 10:37:27

Linux是参照Unix思想设计的,理解掌握Linux必须按照Unix思维来进行。思想性的转变比暂时性的技术提高更有用,因为他能帮助你加快学习速度。

admin 发表于 2015-2-6 10:51:54

对于英语不是很好的读者红旗 Linux、中标Linux这些中文版本比较适合。现在一些Linux网站有一些Linux版本的免费下载,这里要说的是并不适合Linux初学者。

因胸联盟 发表于 2015-3-4 22:30:27

最好先搜寻一下论坛是否有您需要的文章。这样可以获得事半功倍的效果。

柔情似水 发表于 2015-3-11 21:27:58

眼看这个学期的Linux课程已经告一段落了,我觉得有必要写一遍心得体会来总结一下这学期对着门课程的学习。

不帅 发表于 2015-3-19 15:25:51

然我们对Linux的学习首先是通过对它的产生,发展,到今天仍然在不断完善开始的。

精灵巫婆 发表于 2015-3-29 03:57:40

甚至目前许多应用软件都是基于它的。可是没有哪一个系统是十分完美的。
页: [1]
查看完整版本: 带来一篇Python 代码优化罕见技能