Python进阶系列—(1)列表与元组
一、列表和元组基础
列表和元组,都是一个可以放置任意数据类型的有序集合。
1、区别
列表:动态,长度大小不固定(mutable)
元组:静态,长度大小固定,无法增删(immutable)
2、操作
对元组进行操作,实际创建了一个新元组,然后进行填充。
两者可以通过list()和tuple()函数相互转换。
3、常用方法
count(item)
:统计列表 / 元组中 item 出现的次数。
index(item)
表示返回列表 / 元组中 item 第一次出现的索引。
list.reverse()
和 list.sort()
分别表示原地倒转列表和排序(注意,元组没有内置的这两个函数)。
reversed()
和 sorted()
同样表示对列表 / 元组进行倒转和排序,reversed() 返回一个倒转后的迭代器。sorted() 返回排好序的新列表。
二、存储方式差异
列表是动态的,需要存储指针,指向对应的元素(上述为int型,8字节)。
另外,由于列表可变,需要额外存储以及分配的长度大小(8字节),这样可以试试追踪列表空间的使用情况,当空间不足时,及时分配额外内存。
分配机制:over-allocating,增加/删除都为O(1)。
而元组长度大小固定,元素不可变,所以存储空间固定。
具体分析:
l = []
l.__sizeof__() // 空列表的存储空间为40字节
40
l.append(1)
l.__sizeof__()
72 // 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2)
l.__sizeof__()
72 // 由于之前分配了空间,所以加入元素2,列表空间不变
l.append(3)
l.__sizeof__()
72 // 同上
l.append(4)
l.__sizeof__()
72 // 同上
l.append(5)
l.__sizeof__()
104 // 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间
三、性能比较
元组比列表轻量一些,总体性能略优于列表。
python在后台,会对静态数据做一些资源缓存(resource caching)。 对于一些静态变量,比如元组,如果不被使用并且占用空间不大时,会暂时缓存这部分内存。
**初始化**
一个相同元素列表和元组所用时间:
python3 -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop
**索引操作**
,差别非常小:
python3 -m timeit -s 'x=[1,2,3,4,5,6]' 'y=x[3]'
10000000 loops, best of 5: 22.2 nsec per loop
python3 -m timeit -s 'x=(1,2,3,4,5,6)' 'y=x[3]'
10000000 loops, best of 5: 21.9 nsec per loop
注:此处使用timeit标准库,默认会关掉垃圾回收机制,使时间计算更加准确。不过,好像windows平台有限制,初始化时时间相差不大,在linux和mac里比较明显。
四、使用场景
1、如果存储的数据和数量不变,适合使用元组。
比如一个函数返回一个地点的经纬度。
2、如果存储的数据或数量可变,适合用列表。
五、创建空列表效率
# 创建空列表
# option A
empty_list = list()
# option B
empty_list = []
A方式优于B方式,原因在于list()是一个function call,会创建stack。反观[]是一个内置的C函数,可被直接调用,效率高。
六、说明
l = [1,2,3]消耗64字节,而l.append(1),l.append(2),l.append(2)消耗72字节?
因为列表的over-allocate在加入新元素后,解释器判断当前存储空间不够,额外分配的空间。l.append(1),l.append(2),l.append(2)实际分配了四个元素空间,而l = [1,2,3]直接初始化,只分配三个元素空间。