Python - list VS tuple

发布时间 2023-07-14 17:38:17作者: frank_cui

差异一:list可变 vs tuple不可变

列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素(mutable)。

而元组是静态的,长度大小固定,无法增加删减或者改变(immutable)。

# Jupyter格式

tup = (1, 2, 3, 4)
new_tup = tup + (5, ) # 创建新的元组new_tup,并依次填充原元组的值
new_tup
(1, 2, 3, 4, 5)

l = [1, 2, 3, 4]
l.append(5) # 添加元素5到原列表的末尾
l
[1, 2, 3, 4, 5]

 

差异二:存储方式的差异

2.1 初始化相同个数的元素,存储空间就有差异

对列表和元组,我们放置了相同的元素,但是元组的存储空间,却比列表要少 16 字节。

l = [1, 2, 3]
l.__sizeof__()
64

tup = (1, 2, 3)
tup.__sizeof__()
48

原因是:

由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。

 

2.2 增加/删减操作时,list存储空间存在over-allocating

列表空间分配的过程。我们可以看到,为了减小每次增加 / 删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些。

这样的机制(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个元素的空间

 

差异三:性能差异

 元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表。

 

 

补充:list() vs []

想创建一个空的列表,我们可以用下面的 A、B 两种方式,请问它们在效率上有什么区别吗?我们应该优先考虑使用哪种呢?

  • 区别主要在于list()是一个function call,Python的function call会创建stack,并且进行一系列参数检查的操作,比较expensive,
  • 反观[]是一个内置的C函数,可以直接被调用,因此效率高。
# 创建空列表
# option A
empty_list = list()

# option B
empty_list = []