python 数据类型的性能
在python语法精讲中我们讲了python基础数据类型,这里我们来讨论下python两种内置数据类型在各种操作上的时间空间复杂度。
- 列表 list
- 字典 dict
类型 | list | dict |
---|---|---|
索引 | 自然数i | key |
添加 | append、extend、insert | D[key]=v |
删除 | pop、remove | pop |
更新 | ls[i] =v | D[key]=v |
正查 | ls[i]、ls[i:j] | D[key]、copy |
反查 | index(v)、count(v) | 没有 |
其它 | reverse、sort | has_key、update |
列表常用操作
由于列表的随机访问特性,
- var = ls[i] 和 ls[i] = v 这两个操作的执行时间与列表大小无关,均为O(1);
- ls.append(v)执行时间是O(1)
- ls = ls + [ls_1] 执行时间是O(n+k)
生成列表的4个方法
def test_1():
l = []
for i in range(1000):
l = l + [i]
def test_2():
l = []
for i in range(1000):
l.append(i)
def test_3():
l = [i for i in range(1000)]
def test_4():
l = list(range(1000))
使用timeit模块对函数计时
from timeit import Timer
t1 = Timer('test_1()','from __main__ import test_1')
print('concat %f s\n' % t1.timeit(number=1000))
t2 = Timer('test_2()','from __main__ import test_2')
print('append %f s\n' % t2.timeit(number=1000))
t3 = Timer('test_3()','from __main__ import test_3')
print('comprehension %f s\n' % t3.timeit(number=1000))
t4 = Timer('test_4()','from __main__ import test_4')
print('list %f s\n' % t4.timeit(number=1000))
输出结果:
concat 1.138047 s
append 0.049819 s
comprehension 0.028476 s
list 0.012456 s
列表基本操作的复杂度
操作 | 复杂度 |
---|---|
index[ ] | O(1) |
append | O(1) |
pop | O(1) |
pop(i) | O(n) |
insert(i) | O(n) |
sort | O(n log n) |
我们可以看到pop这两种操作的复杂度差别非常的大。原因在于,从中部移除元素的话,要把移除元素后面的元素全部向前挪位复制一遍,这种方法保证列表按索引取值和赋值操作很快,是O(1)。
有兴趣的可以用上面的方法来验证一下pop指令的两种删除的操作复杂度;
ls = list(range(10000))
pop_one = timeit.Timer('ls.pop(1)', 'from __main__ import ls')
print('%f s\n' % pop_one.timeit(number=1000))
pop_end = timeit.Timer('ls.pop()', 'from __main__ import ls')
print('%f s\n' % pop_end.timeit(number=1000))
字典数据类型
字典与列表不同,字典是根据键(key)来获取数据项的。
所以字典存入数据和查找数据都是O(1);判断是否在dict.keys()(是否在字典的键中)的操作也是O(1)复杂度。
字典中的数据的存储是无序的。