Python代码性能优化技巧
代码优化能够让程序运行更快,优化通常包含两方面:减小代码的体积,提高代码的运行效率。
1 优化包含多个判断语句顺序
将相对逻辑比较简单的放在前面优先判断,对于and,应该把满足条件少的放在前面,
对于or,把满足条件多的放在前面
from timeit import timeit
#判断语句顺序不同 代码执行的效率也不一样
print timeit('[i for i in range(1000) if 10 <i<20 or 200<i<1000]', number=1000)
print timeit('[i for i in range(1000) if 200<i<1000 or 10<i<20]', number=1000)
print timeit('[i for i in range(1000) if i<990 and i%3==0]', number=1000)
print timeit('[i for i in range(1000) if i%3==0 and i<990]', number=1000)
2 利用return 优化条件判断缩进
def role_fun(role):
if role.get_level() >= 0:
if role.get_money() >= 100:
if role.get_reputation() >= 999:
role.do_buy()
#将上面这种写法优化为
def opt_role_fun(role):
if role.get_level < 0:
return
if role.get_money() < 100:
return
if role.get_reputation() < 999:
return
role.do_buy()
类似的,当遇到for + 条件判断时,可以用continue优化缩进
def opt_role_fun():
for role in cGameServer.GetAllRole():
if role.get_level() >= 0:
if role.get_money() >= 100:
if role.get_reputation() >= 999:
role.do_buy()
#将上面代码优化
def opt_role_fun():
for role in cGameServer.GetAllRole():
if role.get_level < 0:
continue
if role.get_money() < 100:
continue
if role.get_reputation() < 999:
continue
role.do_buy()
3 对循环优化
尽量减少循环过程中的计算量,有多重循环的尽量将内层的计算提到上一层
from time import time
L1 = [i for i in range(1000)]
L2 = [i for i in range(10)]
for _ in range (1000):
for x in range(len(L1)):
for y in range(len(L2)):
v=L1[x]+L2[y]
#将循环次数多的放最外层,避免在循环体内最重复的运算
#Python2 使用xrange 代替range
length_1=len(L1)
length_2=len(L2)
for _ in xrange (1000):
for x in xrange(length_2):
temp=L2[x]
for y in xrange(length_1):
v=temp+L1[y]
4 查找次数比较多,尽量使用dict或者set
python dict和set都是使用hash表来实现,查找元素的时间复杂度是O(1)
而 list 实际是个数组,查找需要遍历整个 list,其复杂度为 O(n),因此对成员查找访问等操作字典、集合要比 list 更快
from time import time
t = time()
L1 = ['a','b','c','Python','c++']
L2 = [chr(i) for i in range(97,123)]
#L2= dict.fromkeys(L2,True) #这里将list转成dict,大大提高代码效率
l= []
for _ in range (1000000):
for x in L1:
if x not in L2:
l.append(x)
print "total run time:"
print time()-t
5 set 和 list
set 的 union,intersection,difference 操作要比 list 的迭代要快。因此如果涉及到求 list 交集,并集或者差的问题可以转换为 set 来操作。
L1 = ['a','b','c','Python','c++']
L2 = [chr(i) for i in range(97,123)]
for _ in xrange(1000):
ret = list(set(L1)&set(L2))
#set 常见用法
set(L1)|set(L2) #union L1、L2所有元素的集合
set(L1)&set(L2) #intersection L1、L2交集
set(L1)-set(L2) #difference L1中非L2的元素集合
6 使用列表解析和生成器表达式
[chr(i) for i in range(97,123)]
[(x,y) for x in range(3) for y in range(10)]
#慎用这种
{key:val for key in [chr(s) for s in range(97,123)] for val in range(97,123)}
7 对字符串使用的优化
python 中的字符串对象是不可改变的,因此对任何字符串的操作如拼接,修改等都将产生一个新的字符串对象,而不是基于原字符串,因此这种持续的 copy 都会在一定程度上影响 python 的性能
<1>字符串的拼接尽可能使用join 而不是 ‘+’
s = ""
# 尽量避免:
for x in str_list:
s += func(x)
# 而是使用:
s_list = [func(elt) for elt in str_list]
s = ''.join(s_list)
<2>同时可以使用内置函数或者正则表达式的时候,优先选择后者比如str.startswith、str.endswith
<3>格式化字符串方式
#常用的几种格式化字符串方式 效率略有不同 按照速度排列如下
#选择合适自己的一种方式 保持代码风格一致性 个人比较喜欢%
out = "<html>" + head + prologue + query + tail + "</html>"
out = "<html>%s%s%s%s</html>" % (head, prologue, query, tail)
out = "<html>{}{}{}{}</html>".format(head,prologue,query,tail)
8 浅拷贝
生成一个序列副本,可以用copy与切片特性
切片就是序列抽取部分的操作,切片操作得到的对象和原对象是不同的对象,切片相当于浅拷贝
import copy
List = [0,1,2,3]
L1 = copy.copy(List)
L2 = List[:] #使用切片的效率更高
9 变量值交换
# x, y 直接交换
x, y = y, x
# 而不是借助中间变量
t = x; x = y; y = t
10 使用if is/ if is not
if xx == True: -->if xx is True:
if xx != True: --> if xx if not True:
11 级联比较
start < x < end #这种写法效率略高,而且可读性更佳
12 善于利用for+else
def find_card(card_id)
has_found = False
for idx, card in enumerate(card_pool):
if card_id == card.get_card_id():
#找到的处理逻辑
pass
has_found = True
break
else:
# 没有找到的处理逻辑
pass
#其他...
pass