分类: Python/Ruby
2016-12-27 16:44:30
相信用python的同学不少,本人也一直对python情有独钟,毫无疑问python作为一门解释性动态语言没有那些编译型语言高效,但是python简洁、易读以及可扩展性等特性使得它大受青睐。
工作中很多同事都在用python,但往往很少有人关注它的性能和惯用法,一般都是现学现用,毕竟python不是我们的主要语言,我们一般只是使用它来做一些系统管理的工作。但是我们为什么不做的更好呢?python zen中有这样一句:There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. 大意就是python鼓励使用一种最优的方法去完成一件事,这也是和ruby等的一个差异。所以一种好的python编写习惯个人认为很重要,本文就重点从性能角度出发对python的一些惯用法做一个简单总结,希望对大家有用~
提到性能,最容易想到的是降低复杂度,一般可以通过测量代码回路复杂度(cyclomatic complexitly)和Landau符号(大O)来分析, 比如dict查找是O(1),而列表的查找却是O(n),显然数据的存储方式选择会直接影响算法的复杂度。
1. 在列表中查找:
对于已经排序的列表考虑用bisect模块来实现查找元素,该模块将使用二分查找实现
1 2 3 4 5 | def find(seq, el) : pos = bisect(seq, el) if pos = = 0 or ( pos = = len (seq) and seq[ - 1 ] ! = el ) : return - 1 return pos - 1 |
而快速插入一个元素可以用:
1 | bisect.insort( list , element) |
这样就插入元素并且不需要再次调用 sort() 来保序,要知道对于长list代价很高.
2. set代替列表:
比如要对一个list进行去重,最容易想到的实现:
1 2 3 4 5 | seq = [ 'a' , 'a' , 'b' ] res = [] for i in seq: if i not in res: res.append(i) |
显然上面的实现的复杂度是O(n2),若改成:
1 2 | seq = [ 'a' , 'a' , 'b' ] res = set (seq) |
复杂度马上降为O(n),当然这里假定set可以满足后续使用。
另外,set的union,intersection,difference等操作要比列表的迭代快的多,因此如果涉及到求列表交集,并集或者差集等问题可以转换为set来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大。
3. 使用python的collections模块替代内建容器类型:
collections有三种类型:
列表是基于数组实现的,而deque是基于双链表的,所以后者在中间or前面插入元素,或者删除元素都会快很多。
defaultdict为新的键值添加了一个默认的工厂,可以避免编写一个额外的测试来初始化映射条目,比dict.setdefault更高效,引用python文档的一个例子:
#使用profile stats工具进行性能分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | >>> from pbp.scripts.profiler import profile, stats >>> s = [( 'yellow' , 1 ), ( 'blue' , 2 ), ( 'yellow' , 3 ), ... ( 'blue' , 4 ), ( 'red' , 1 )] >>> @profile( 'defaultdict' ) ... def faster(): ... d = defaultdict( list ) ... for k, v in s: ... d[k].append(v) ... >>> @profile( 'dict' ) ... def slower(): ... d = {} ... for k, v in s: ... d.setdefault(k, []).append(v) ... >>> slower(); faster() Optimization: Solutions [ 306 ] >>> stats[ 'dict' ] { 'stones' : 16.587882671716077 , 'memory' : 396 , 'time' : <span style = "box-sizing:border-box;font-weight:700;" > 0.35166311264038086 < / span>} >>> stats[ 'defaultdict' ] { 'stones' : 6.5733464259021686 , 'memory' : 552 , 'time' : <span style = "box-sizing:border-box;font-weight:700;" > 0.13935494422912598 < / span>} |
可见性能提升了快3倍。defaultdict用一个list工厂作为参数,同样可用于内建类型,比如long等。