对于每一门编程语言来说,数据结构都是其根基。 Python 的基本数据结构包括列表,元组,字典,集合,字符串,栈,队列,堆,树和图。
通过这篇文章,你可以了解 Python 基础数据结构中的列表,元组的使用方法和应用场景。
列表和元组
定义
列表和元组,都是一个可以放置任意数据类型的有序集合。在Java 等绝大多数编程语言中,集合的数据类型必须一致。不过,对于 Python 的列表和元组来说,可以存储不同的数据类型。
list_zhihu = [1, 'zhihu']
tup_zhihu = ('zhihu', 5)
区别
列表是动态的,长度不固定,可以随意增删或改变元素,不会创建新的列表
元组是静态的,长度固定,无法随意增删或改变,如果要对已有元组改变,只能重新开辟内存,创建新的元组
tup_zhihu = ('zhihu', 5)
new_tup_zhihu = tup_zhihu + (6, )
net_tup_zhihu
('zhihu', 5, 6)
list_zhihu = [2, 3, 6]
list_zhihu.append(5)
list_zhihu
[2, 3, 6, 5]
基本操作
Python 中的列表和元组都支持负数索引,-1 表示最后一个元素,以此类推。
tup_zhihu = (1, 2, 3, 5)
tup_zhihu[-1]
5
list_zhihu = [1, 2, 3, 4]
list_zhihu[-2]
3
支持切片操作
tup_zhihu = (1, 2, 3, 5)
tup_zhihu[1:3]
(2,3)
list_zhihu = [1, 2, 3, 4]
list_zhihu[1:3]
[2,3]
可以随意嵌套
tup_zhihu = ((1, 2, 3), (4, 5, 6))
list_zhihu = [[1, 2, 3,], [4, 5, 6]]
列表和元组可以通过 list() 和 tuple() 相互转换
list((2, 3, 4))
tuple([2, 3, 4])
其他常用函数
list_zhihu = [1, 2, 3, 4]
//统计 item 出现的次数
list_zhihu.count(3)
//返回item 第一次出现的索引
list_zhihu.index(4)
//原地倒转列表
list_zhihu.reverse()
//原地排序列表
list_zhihu.sort()
存储方式
列表和元组一个动态,可变的,一个静态,不可变的,这种特性势必会影响其存储结构,先来看一个例子。
list_zhihu = [1, 4]
list_zhihu.__sizeof__()
56
tup_zhihu = (1, 4)
tup_zhihu.__sizeof__()
40
对于上面那个例子中的元组和列表,我们存储了相同的元素,列表所占用的空间比元组多了 16 个字节,为什么?
这是因为列表是动态的,所以还需要存储一个指向对应元素的指针(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。所以存储相同的整数元素,列表比元组多 16 个字节。
list_zhihu = []
list_zhihu.__sizeof__() // 空列表的存储空间为40字节
40
list_zhihu.append(45)
72 // 加入了元素 45 之后,列表为其分配了可以存储 4 个元素的空间 (72 - 40) / 8 = 4
list_zhihu.append(35)
list_zhihu.__sizeof__()
72 // 同上
list_zhihu.append(55)
list_zhihu.__sizeof__()
72 // 同上
list_zhihu.append(65)
list_zhihu.__sizeof__()
104 // 加入元素 65 之后,列表的空间不足,所以又额外分配了可以存储 4 个元素的空间
通过上述代码,我们可以总结出列表扩容的规律,为了减小每次增加 / 删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些,这样的机制保证了其操作的高效性。
列表和元组的性能
根据分析列表和元组的存储方式,我们可以得出结论,元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表。
对于元组,因为垃圾回收机制的存在,如果它不被使用并且占用空间不大时,Python 会暂时缓存这部分内存。这样,下次我们再创建同样大小的元组时,Python 就可以不用再向操作系统发出请求,去寻找内存,而是可以直接分配之前缓存的内存空间,这样就能大大加快程序的运行速度。从下面执行代码可以看到,元组的初始化速度,要比列表快 5 倍左右。
python3 -m timeit 'x=(1,2,3,4,5,6)'
50000000 loops, best of 5: 8.59 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 46.5 nsec per loop
针对索引操作,列表和元组速度基本相同,可以忽略不计
python3 -m timeit -s 'x=[1,2,3,4,5,6]' 'y=x[3]'
10000000 loops, best of 5: 23 nsec per loop
python3 -m timeit -s 'x=(1,2,3,4,5,6)' 'y=x[3]'
10000000 loops, best of 5: 23 nsec per loop
如果数据需要增加,删除或改变,用列表显然是更优的,毕竟元组需要通过新建一个元组完成
列表和元组的使用场景
-
如果存储的数据和数量不变,比如你有一个函数,需要返回的是一个人的身高和体重,然后直接传给前端渲染,那么肯定选用元组更合适。
-
如果存储的数据或数量是可变的,比如一个关注功能,要统计一个用户在一周之内新关注了那些用户,那么则用列表更合适。
总结
列表和元组都是有序的,可以存储任意数据类型的集合,区别主要在于下面这两点。
- 列表是动态的,长度可变,可以随意增删减或改变元素。列表的存储空间略大于元组,性能略逊于元组。
- 元组是静态的,长度大小固定,不可以对元素进行增删或者改变操作,元组相对于列表更加轻量级,性能稍优。