1. 在列表中查找:
对于已经排序的列表考虑用bisect模块来实现查找元素,该模块将使用二分查找实现
1 |
def find(seq, el) : |
2 |
pos = bisect(seq, el) |
3 |
if pos = = 0 or ( pos = = len (seq) and seq[ - 1 ] ! = el ) : |
4 |
return - 1 |
5 |
return pos - 1 |
而快速插入一个元素可以用:
1 |
bisect.insort( list , element) |
这样就插入元素并且不需要再次调用 sort() 来保序,要知道对于长list代价很高.
2. set代替列表:
比如要对一个list进行去重,最容易想到的实现:
1 |
seq = [ 'a' , 'a' , 'b' ] |
2 |
res = [] |
3 |
for i in seq: |
4 |
if i not in res: |
5 |
res.append(i) |
显然上面的实现的复杂度是O(n2),若改成:
1 |
seq = [ 'a' , 'a' , 'b' ] |
2 |
res = set (seq) |
复杂度马上降为O(n),当然这里假定set可以满足后续使用。
另外,set的union,intersection,difference等操作要比列表的迭代快的多,因此如果涉及到求列表交集,并集或者差集等问题可以转换为set来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大。
3. 使用python的collections模块替代内建容器类型:
collections有三种类型:
列表是基于数组实现的,而deque是基于双链表的,所以后者在中间or前面插入元素,或者删除元素都会快很多。
defaultdict为新的键值添加了一个默认的工厂,可以避免编写一个额外的测试来初始化映射条目,比dict.setdefault更高效,引用python文档的一个例子:
#使用profile stats工具进行性能分析
01 |
>>> from pbp.scripts.profiler import profile, stats |
02 |
>>> s = [( 'yellow' , 1 ), ( 'blue' , 2 ), ( 'yellow' , 3 ), |
03 |
... ( 'blue' , 4 ), ( 'red' , 1 )] |
04 |
>>> @profile( 'defaultdict' ) |
05 |
... def faster(): |
06 |
... d = defaultdict( list ) |
07 |
... for k, v in s: |
08 |
... d[k].append(v) |
09 |
... |
10 |
>>> @profile( 'dict' ) |
11 |
... def slower(): |
12 |
... d = {} |
13 |
... for k, v in s: |
14 |
... d.setdefault(k, []).append(v) |
15 |
... |
16 |
>>> slower(); faster() |
17 |
Optimization: Solutions |
18 |
[ 306 ] |
19 |
>>> stats[ 'dict' ] |
20 |
{ 'stones' : 16.587882671716077 , 'memory' : 396 , |
21 |
'time' : <strong> 0.35166311264038086 < / strong>} |
22 |
>>> stats[ 'defaultdict' ] |
23 |
{ 'stones' : 6.5733464259021686 , 'memory' : 552 , |
24 |
'time' : <strong> 0.13935494422912598 < / strong>} |
可见性能提升了快3倍。defaultdict用一个list工厂作为参数,同样可用于内建类型,比如long等。
除了实现的算法、架构之外,python提倡简单、优雅。所以正确的语法实践又很有必要,这样才会写出优雅易于阅读的代码。
(1)用join代替 '+' 操作符,后者有copy开销;
(2)同时当对字符串可以使用正则表达式或者内置函数来处理的时候,选择内置函数。如str.isalpha(),str.isdigit(),str.startswith((‘x’, ‘yz’)),str.endswith((‘x’, ‘yz’))
(3)字符格式化操作优于直接串联读取:
1 |
str = "%s%s%s%s" % (a, b, c, d) # efficient |
2 |
str = " " + a + b + c + d + " " # slow |
2. 善用list comprehension(列表解析) & generator(生成器) & decorators(装饰器),熟悉itertools等模块:
(1) 列表解析,我觉得是python2中最让我印象深刻的特性,举例1:
01 |
>>> # the following is not so Pythonic |
02 |
>>> numbers = range ( 10 ) |
03 |
>>> i = 0 |
04 |
>>> evens = [] |
05 |
>>> while i < len (numbers): |
06 |
>>> if i % 2 = = 0 : evens.append(i) |
07 |
>>> i + = 1 |
08 |
>>> [ 0 , 2 , 4 , 6 , 8 ] |
09 |
10 |
>>> # the good way to iterate a range, elegant and efficient |
11 |
>>> evens = [ i for i in range ( 10 ) if i % 2 = = 0 ] |
12 |
>>> [ 0 , 2 , 4 , 6 , 8 ] |
举例2:
1 |
def _treament(pos, element): |
2 |
return '%d: %s' % (pos, element)<br> |
01 |
f = open ( 'test.txt' , 'r' ) |
02 |
if __name__ = = '__main__' : |
03 |
#list comps 1 |
04 |
print sum ( len (word) for line in f for word in line.split()) |
05 |
#list comps 2 |
06 |
print [(x + 1 , y + 1 ) for x in range ( 3 ) for y in range ( 4 )] |
07 |
#func |
08 |
print filter ( lambda x: x % 2 = = 0 , range ( 10 )) |
09 |
#list comps3 |
10 |
print [i for i in range ( 10 ) if i % 2 = = 0 ] |
11 |
#list comps4 pythonic |
12 |
print [_treament(i, el) for i, el in enumerate ( range ( 10 ))] |
13 |
14 |
output: |
15 |
24 |
16 |
[( 1 , 1 ), ( 1 , 2 ), ( 1 , 3 ), ( 1 , 4 ), ( 2 , 1 ), ( 2 , 2 ), ( 2 , 3 ), ( 2 , 4 ), ( 3 , 1 ), ( 3 , 2 ), ( 3 , 3 ), ( 3 , 4 )] |
17 |
[ 0 , 2 , 4 , 6 , 8 ] |
18 |
[ 0 , 2 , 4 , 6 , 8 ] |
19 |
[ '0: 0' , '1: 1' , '2: 2' , '3: 3' , '4: 4' , '5: 5' , '6: 6' , '7: 7' , '8: 8' , '9: 9' ]<br> |
没错,就是这么优雅简单。
(2) 生成器表达式在python2.2引入,它使用'lazy evaluation'思想,因此在使用内存上更有效。引用python核心编程中计算文件中最长的行的例子:
1 |
f = open ( '/etc/motd, ' r') |
2 |
longest = max ( len (x.strip()) for x in f) |
3 |
f.close() |
4 |
return longest |
这种实现简洁而且不需要把文件文件所有行读入内存。
(3) python在2.4引入装饰器,又是一个让人兴奋的特性,简单来说它使得函数和方法封装(接收一个函数并返回增强版本的函数)更容易阅读、理解。'@'符号是装饰器语法,你可以装饰一个函数,记住调用结果供后续使用,这种技术被称为memoization的,下面是用装饰器完成一个cache功能:
01 |
import time |
02 |
import hashlib |
03 |
import pickle |
04 |
from itertools import chain |
05 |
cache = {} |
06 |
def is_obsolete(entry, duration): |
07 |
return time.time() - entry[ 'time' ] > duration |
08 |
09 |
def compute_key(function, args, kw): |
10 |
#序列化/反序列化一个对象,这里是用pickle模块对函数和参数对象进行序列化为一个hash值 |
11 |
key = pickle.dumps((function.func_name, args, kw)) |
12 |
#hashlib是一个提供MD5和sh1的一个库,该结果保存在一个全局字典中 |
13 |
return hashlib.sha1(key).hexdigest() |
14 |
15 |
def memoize(duration = 10 ): |
16 |
def _memoize(function): |
17 |
def __memoize( * args, * * kw): |
18 |
key = compute_key(function, args, kw) |
19 |
20 |
# do we have it already |
21 |
if (key in cache and |
22 |
not is_obsolete(cache[key], duration)): |
23 |
print 'we got a winner' |
24 |
return cache[key][ 'value' ] |
25 |
26 |
# computing |
27 |
result = function( * args, * * kw) |
28 |
# storing the result |
29 |
cache[key] = { 'value' : result, - |
30 |
'time' : time.time()} |
31 |
return result |
32 |
return __memoize |
33 |
return _memoize |
34 |
35 |
@memoize () |
36 |
def very_very_complex_stuff(a, b, c): |
37 |
return a + b + c |
38 |
39 |
print very_very_complex_stuff( 2 , 2 , 2 ) |
40 |
print very_very_complex_stuff( 2 , 2 , 2 ) |
41 |
42 |
43 |
@memoize ( 1 ) |
44 |
def very_very_complex_stuff(a, b): |
45 |
return a + b |
46 |
47 |
print very_very_complex_stuff( 2 , 2 ) |
48 |
time.sleep( 2 ) |
49 |
print very_very_complex_stuff( 2 , 2 ) |
1 |
<br> |
运行结果:
6
we got a winner
6
4
4
装饰器在很多场景用到,比如参数检查、锁同步、单元测试框架等,有兴趣的人可以自己进一步学习。
3. 善用python强大的自省能力(属性和描述符):自从使用了python,真的是惊讶原来自省可以做的这么强大简单,关于这个话题,限于内容比较多,这里就不赘述,后续有时间单独做一个总结,学习python必须对其自省好好理解。
使用多重赋值来swap元素:
1 |
x, y = y, x # elegant and efficient |
而不是:
1 |
temp = x |
2 |
x = y |
3 |
y = temp |
9. 三元操作符(python2.5后):V1 if X else V2,避免使用(X and V1) or V2,因为后者当V1=""时,就会有问题。
10. python之switch case实现:因为switch case语法完全可用if else代替,所以python就没 有switch case语法,但是我们可以用dictionary或lamda实现:
1 |
switch case结构: |
1 |
switch (var) |
2 |
{ |
3 |
case v1: func1(); |
4 |
case v2: func2(); |
5 |
... |
6 |
case vN: funcN(); |
7 |
default: default_func(); |
8 |
} |
dictionary实现:
1 |
values = { |
2 |
v1: func1, |
3 |
v2: func2, |
4 |
... |
5 |
vN: funcN, |
6 |
} |
7 |
values.get(var, default_func)() |
lambda实现:
1 |
{ |
2 |
'1' : lambda : func1, |
3 |
'2' : lambda : func2, |
4 |
'3' : lambda : func3 |
5 |
}[value]() |
用try…catch来实现带Default的情况,个人推荐使用dict的实现方法。
这里只总结了一部分python的实践方法,希望这些建议可以帮助到每一位使用python的同学,优化性能不是重点,高效解决问题,让自己写的代码更加易于维护,更加pythonic!