python的性能和计算能力一直被吐槽,从未被超越,越是这样越应该反省平常在使用的过程当中应当注意哪些优化措施,能让我们的程序运行得更快。
总结一下自己平常在使用 python的过程当中所导致的性能杀死:
1.内置类型方面:
python 内部的变量是分为可变类型和不可变类型,这里说的可变与不可变不是说变量与常量的区别,而是关于对象的生命周期问题。
假设一个python 原生字符串:
_str = "Hello" id ( str ) = 33738016 #随机值 _str + = "World" id ( str ) = 33738056 |
从上面可以看出, id() 值是变了,意味着什么?
id 值的改变意味着这个对象的内存地址改变,也就是说,执行的 += 操作是将原来的字符串拼接完成后拷贝到新的地址。
可以看到两次id值相减为40,也就是5个 byte
所以应该尽量避免过多的 += 操作
如果需要拼接很多字符串的话尽量使用格式化字符串方式或者使用 str的 join方法
s = "%s,%s" % (str_1, str_2) s = "".join([str_1, str_2]) |
总结一下哪些内置类型是变化哪些是不变化的:
dict, tuple, set 是不变是,字符串,其他变量是变化的
如果一个dict 或者 set 里面包含字符串或者其他变量,对dict, set的操作不会引起自身变化,但是其引用对象是变化的
2.一些迭代对象
在python2.x 版本里面,很多地方会使用到 for e in range(...) 形式或者 for e in E.items() 形式
这里需要注意的是,在 range()或者 items() 等操作过程中。是会在中间过程生成一个临时的 list
如果这个list很大的话将会让你的程序陷入噩梦
解决办法就是在这种for 循环中,使用可迭代对象
range 可由 xrange 替代,items 可由 iteritems 替代,还有诸如 iterkeys等
3.类对象
python的函数调用是很消耗资源的,所以尽量避免过多的函数调用,也可以适当使用lambda表达式替换
如果某个类需要创建大量的实例,而这个实例在运行过程中不会动态的添加其他的属性,那么可以使用内置的
__slot__方式
在类中定义了 __slot__ = ['attr1', 'attr2']
表示这个类只能拥有这些属性,这样避免在类生成过程中会为类自己生成 __dict__属性,这样可以减少很多的资源消耗
尽量少使用类变量,每个函数完成自己的功能,适当让耦合度降低
4.循环优化
多数的计算都发生在循环过程中,所以计算瓶颈可能出现在循环的地方。将循环适当的优化可以做到事半功倍的效果
while 循环使用 while 1: 而不是 while True:
使用 while True python也会自动给你转化为 while 1,避免每次循环都要转化一次岂不是更好
将计算长度移到循环外
很多同学喜欢这样使用:
for x in range ( 0 , len ( L )): do(x) |
这样使用的时候会导致每一次循环都计算一次长度,浪费了很多
如果要生成的结果是一个list 的话,最好的方式不是使用每次将满足条件的值 append 到这个list
这样使用可以减少很多资源消耗
return [ func(e) for e in L ] |
5.一些其他的操作
对于字典的操作,获取一个值最好使用内置 get() 而不是按照下标来使用
否则需要写成这样:
try : X = d[key] except ValueError : X = None 或者 if d.has_key(key): X = d[key] else : X = None |
这样的代码谁都不喜欢看到
在需要使用到 in 操作的地方,使用 set 或者 dict 而不是 list
这样只需要O(1)时间就可以判断,而 list 会挨个比较
在可能需要对list 里面的值做排序的地方尽量使用 bisect
没有看过 bisect 的实现,内部是用的 C模块,估计使用的是红黑树,虽然插入操作需要的时间多一些,但是对于排序号的列表,后期优势就很大了
在使用到多线程的地方,对一个列表的操作使用 collections.deque()
collections.deque 支持多线程的原子操作,可以减少你自己加锁解锁的代码复杂性
在需要读取文件的操作中,使用迭代对象对文件的每一行做操作而不是用 readlines() 或者一次性读取后按行操作
使用方式做好是这样:
with open ( "file" , "rb" ) as fp: for line in fp: do(line) |
with 语句2.x版本在文件开头引入
from __future__ import with_statement
能想到的就是这些了,欢迎补充