# 奇奇怪怪的python优化点
# == or os
比较操作符'is'的速度效率,通常要优于'=='。因为'is'操作符不能被重载,这样,Python 就不需要去寻找,程序中是否有其他地方重载了比较操作符,并去调用。执行比较操作符'is',就仅仅是比较两个变量的 ID 而已。但是''操作符却不同,执行a == b相当于是去执行a.eq(b),而 Python 大部分的数据类型都会去重载__eq__这个函数,其内部的处理通常会复杂一些。比如,对于列表,__eq__函数会去遍历列表中的元素,比较它们的顺序和值是否相等。
# += or join
实际上由于字符串末尾大多也有空闲空间(内存对齐)的缘故直接做+=也不见得会触发扩容导致多次复制,性能没有想象中的那么差,但是实验下来join的效率基本都是+=的数倍。
import random
import time
def test(list_len):
l = []
for i in range(list_len):
# l.append(''.join([str(random.randint(1,10)) for _ in range(random.randint(1,20))]))
l.append(str(i))
print(f'len(l): {len(l)}')
start = time.time()
s1 = ''
for i in l:
s1 += i
end = time.time()
add_time = end - start
print(f'add: {add_time}')
start = time.time()
s2 = ''.join(l)
end = time.time()
join_time = end - start
print(f'join: {join_time}')
print(f'add/join: {add_time/join_time}')
for i in range(0, 7):
test(10**i)
"""
len(l): 1
add: 7.152557373046875e-07
join: 9.5367431640625e-07
add/join: 0.75
len(l): 10
add: 2.1457672119140625e-06
join: 9.5367431640625e-07
add/join: 2.25
len(l): 100
add: 1.33514404296875e-05
join: 2.1457672119140625e-06
add/join: 6.222222222222222
len(l): 1000
add: 7.009506225585938e-05
join: 1.5974044799804688e-05
add/join: 4.388059701492537
len(l): 10000
add: 0.0007255077362060547
join: 0.0001995563507080078
add/join: 3.6356033452807646
len(l): 100000
add: 0.006604194641113281
join: 0.0031599998474121094
add/join: 2.089935113927871
len(l): 1000000
add: 0.08632159233093262
join: 0.028325557708740234
add/join: 3.0474807669646315
"""
# list() or []
想创建一个空的列表,我们可以用下面的 A、B 两种方式,请问它们在效率上有什么区别吗?我们应该优先考虑使用哪种呢?可以说说你的理由。
# 创建空列表
# option A
empty_list = list()
# option B
empty_list = []
区别主要在于list()是一个function call,Python的function call会创建stack,并且进行一系列参数检查的操作,比较expensive,反观[]是一个内置的C函数,可以直接被调用,因此效率高。 两者差别还是蛮大的。
>>> import dis
>>> dis.dis('l=list()')
0 0 RESUME 0
1 2 PUSH_NULL
4 LOAD_NAME 0 (list)
6 PRECALL 0
10 CALL 0
20 STORE_NAME 1 (l)
22 LOAD_CONST 0 (None)
24 RETURN_VALUE
>>> dis.dis('l=[]')
0 0 RESUME 0
1 2 BUILD_LIST 0
4 STORE_NAME 0 (l)
6 LOAD_CONST 0 (None)
8 RETURN_VALUE
# s_add = s.add
s = set()
set_add = s.add
for data in source:
set_add(data)
通过在字节码层面减少执行一个LOAD_ATTR去提升一些性能,这个提升应该是observable的,在你的for loop比较大的时候。
# truthy value - container or numeric value
在binomialvariate这个函数里,出现了这么一个判断:
c = _log2(1.0 - p)
if not c:
return x
涉及到Python底层字节码的实现问题,if not c确实比if c == 0要更快。但是我的观点是,运行速度并不是决定一段代码写法的唯一因素。我个人是比较可以接受对于container(比如list,dict,set这种)使用truthy value进行判断的,但是对于numeric value,我觉得if not c是一个很不理想的写法。在语义上,它是非常不明确的。