python内置类型性能分析
timeit模块
timeit模块可以用来测试一小段Python代码的执行速度。
class timeit.Timer(stmt='pass',setup='pass',timer=<timer function>)
Timer是测量小段代码执行速度的类;
stmt参数是要测试的代码语句(statment);
setup参数是运行代码是需要的设置;
timer参数是一个定时器函数,与平台无关。timeit.Timer.timeit(number=1000000)
Timer类中测试语句执行速度的对象方法。number参数是测试代码时的测试次数,默认为1000000次。方法返回执行代码的平均耗时,一个float类型的秒数。
list的操作测试
from timeit import Timer
def t1():
li = []
for i in range (10000):
li.append(i) #append只能添加一个单个元素
def t2():
li=[]
for i in range(10000):
li = li + [i]
def t3():
li = [i for i in range(10000)]
def t4():
li = list(range(10000))
def t5():
li =[]
for i in range(10000):
li.extend([i])#extend可以添加一个列表
timer1 = Timer("t1()","from __main__ import t1")
print("append:",timer1.timeit(1000))
timer2 = Timer("t2()","from __main__ import t2")
print("+:",timer2.timeit(1000))
timer3 = Timer("t3()","from __main__ import t3")
print("i for i in range:",timer3.timeit(1000))
timer4 = Timer("t4()","from __main__ import t4")
print("list(range()):",timer4.timeit(1000))
timer5 = Timer("t5()","from __main__ import t5")
print("extend:",timer5.timeit(1000))
结果:
insert和append
两者都是对python内的列表进行操作,append()方法是值在列表的末尾增加一个数据项,insert()方法是指在某个特定位置前加一个数据项。
通过timeit模块对这两种操作进行比较:
from timeit import Timer
def t6():
li = []
for i in range (10000):
li.append(i) #append只能添加一个单个元素
def t7():
li=[]
for i in range(10000):
li.insert(0,i)
timer6 = Timer("t6()","from __main__ import t6")
print("append:",timer6.timeit(1000))
timer7 = Timer("t7()","from __main__ import t7")
print("insert:",timer7.timeit(1000))
结果:
可以看出,通过insert方法添加数据是非常慢的。
原因如下:Python内的list实现是通过数组实现的,而不是链表的形式。
因此insert()操作的具体流程是:分配的空间足够大时,首先将插入未知之后的元素向后移动相应的位置,然后插入元素,算法的时间复杂度为O(n)。
执行append()操作的具体流程是:分配的空间足够大时,直接将元素插入到最后,因此算法的时间复杂度为O(1)。
如果分配的内存不够,则需要开辟一段新的内存空间将已有的数据复制过去,在进行相应的操作。
在建立空表(或者很小的表)时,系统分配一块能容纳8个元素的存储区;在执行插入操作(insert或append等)时,如果元素区满就换一块4倍大的存储区。但是当时的表已经“很大”是一个实现确定的参数,目标值是50000。引入后一个策略是为了避免出现过多空闲的存储位置。
更多关于python中list的基本实现技术可查看:https://blog.csdn.net/weixin_44953902/article/details/96969579
pop操作测试
x = list(range(2000000))
pop_zero = Timer("x.pop(0)","from __main__ import x")
print("pop_zero:",pop_zero.timeit(10000))
x = list(range(2000000))
pop_end = Timer("x.pop()","from __main__ import x")
print("pop_end:",pop_end.timeit(10000))
测试pop操作:从结果可以看出,pop最后一个元素的效率远远高于pop第一个元素
list和dict不能算作基本数据类型,它只是python封装好的。对于计算机所能使用的基本类型不包括lis和dict。
list内置操作的时间复杂度
操作 | 时间复杂度 | 含义 |
---|---|---|
index x[] | O(1) | 通过地址索引来取值 |
index assignment | O(1) | 通过地址索引来进行赋值 |
append | O(1) | 在列表的后面追加元素 |
pop() | O(1) | 不加参数,表示从尾部往外弹出 |
pop(i) | O(n) | 从指定位置弹出 |
insert(i,item) | O(n) | 在指定位置插入元素 |
del opertor | O(n) | 删除列表中的所有元素,并且是一个一个的删 |
interation | O(n) | 迭代 |
contains(in) | O(n) | 某元素是否在列表当中,必须遍历列表中的所有元素 |
get slice[x:y] | O(k) | 切片操作:k=y-x |
del slice | O(n) | 删除切片操作,删除切片之后,要将后面部分的元素往前移动 |
set slice | O(n+k) | 更改切片,首先删除原列表切片中的数值O(n),再添加k个元素 |
extend([]) | O(k) | 添加列表中的到列表中 |
reverse | O(n) | 逆置 |
concatenate | O(k) | 将两个列表加在一起,k代表第二个列表元素的个数 |
sort | O(nlog(n)) | 排序 |
multiply | O(nk) | 列表的乘法 |
dict内置操作的时间复杂度
操作 | 时间复杂度 | 含义 |
---|---|---|
copy | O(n) | 复制字典:将字典中的所有元素复制一份 |
get item | O(1) | 取字典中的某个键和值 |
set item | O(1) | 重置字典中的某个键和值 |
delete item | O(1) | 删除字典中的某个键和值 |
contains(in) | O(1) | 某键和值是否在字典当中 |
interation | O(n) | 迭代 |