Jerry Ng 2021-08-10
在 Python 中,有四种内置的数据类型可用于存储数据集合。这些内置数据类型分别是:列表(list)、元组(tuple)、集合(set)和字典(dict),它们各自具有不同的特性和用途。
在本文中,我们将深入探讨 Python 中的列表、元组和集合。我们会详细比较它们之间的区别,并讨论在什么场景下应使用哪种数据类型。
由于字典(Dictionary)通过键(key)关联其对应的值(value),其使用场景与仅包含值的列表、元组和集合有着本质的不同,因此本次讨论将不包括字典。
为简化说明,我将集合(Set)和字典(Dictionary)互换使用,因为它们底层都基于哈希表(Hash Table,也称哈希映射 Hash Map)。
为什么我们需要关心这个问题?
在大多数情况下,这些数据类型在应用程序中可以互换使用而不会造成太大问题。
然而,试想一下:如果我们被要求在一个庞大的“干草堆”(haystack)中查找一根“针”(needle),那么从速度和内存效率的角度来看,最高效的方式是什么?
这个“干草堆”应该用列表(List)吗?还是元组(Tuple)?或者为什么不总是使用集合(Set)(或字典 Dictionary)呢?使用它们时又有哪些需要注意的陷阱?
让我们深入探讨!
列表、元组和集合之间的区别
重复元素(Duplicates)
如果我要打个比方,列表和元组就像是 Python 中的亲兄弟,而集合(或字典)则是它们的表亲。
与列表或元组不同,集合不能包含重复元素。换句话说,集合中的元素是唯一的。
set_example = {1, 1, 2, 3, 3, 3}
# 结果: {1, 2, 3}
fruit_set = {'🍎', '🍓', '🍐', '🍎', '🍎', '🍓'}
# 结果: {'🍎', '🍐', '🍓'}
有了这一认识,我们现在就知道:集合也可以用来从列表中去除重复项!
排序顺序(Sorting Order)
你可能听说过这样一句话:“在 Python 中,集合和字典是无序的。”但这句话如今只说对了一半,具体取决于你使用的 Python 版本。
在 Python 3.6 之前,字典和集合不保留插入顺序。例如,如果你在 Python 3.5 中运行以下代码:
# Python 3.5 中的示例
fruit_size = {}
fruit_size['🍎'] = 12
fruit_size['🍐'] = 16
fruit_size['🍇'] = 20
fruit_size
# 输出可能是: {'🍎': 12, '🍇': 20, '🍐': 16}
💡
小贴士:你可以使用 asdf(pyenv 的替代工具)轻松在不同 Python 版本之间切换。
如今,这一说法已经过时好几年了。从 Python 3.7 开始,字典和集合官方保证按照插入顺序进行排序。
顺便提一句,列表和元组始终是有序的对象序列。
可变性(Mutability)
当我们说一个对象是“可变的”(mutable),其实就是一种花哨的说法,意思是该对象的内部状态可以被修改。
这里的关键区别在于:元组是不可变的(immutable,即不可更改),而列表和集合是可变的。
尽管集合是可变的,但我们无法通过索引或切片来访问或修改集合中的任意元素。因此,我们只能向集合中添加新元素,而不能修改已有元素。
请注意,集合中的 update() 方法仅仅表示可以一次性添加多个元素。
索引(Indexing)
列表和元组都支持索引和切片操作,而集合则不支持。
fruit_list = ['🍎', '🍓', '🍐']
fruit_list[1]
# '🍓'
animal_tuple = ('🐶', '🐱', '🐮')
animal_tuple[2]
# '🐮'
vehicle_set = {'🚐', '🏍', '🚗'}
vehicle_set[0]
# TypeError: 'set' object is not subscriptable(集合对象不支持下标访问)
何时使用列表 vs 元组?
正如前面提到的,元组是不可变的,而列表是可变的。同样地,元组本质上是固定大小的,而列表是动态的。
a_tuple = tuple(range(1000))
a_list = list(range(1000))
a_tuple.__sizeof__() # 8024 字节
a_list.__sizeof__() # 9088 字节
使用列表的情况:
- 当你需要对集合进行修改时。
- 当你需要从集合中删除元素或添加新元素时。
使用元组的情况:
- 如果你的数据不应该(或不需要)被更改。
- 元组比列表更快。如果你定义的是一组常量值,并且唯一要做的就是遍历它,那么应优先使用元组。
- 如果你需要将一组元素用作字典的键(key),可以使用元组。因为列表是可变的(属于不可哈希类型),永远不能作为字典的键。
何时使用集合 vs 列表/元组?
由于集合底层使用哈希表作为其数据结构,因此在检查某个元素是否存在于集合中时(例如 x in a_set),集合的速度非常快。
其原理在于:在哈希表中查找一个项目的时间复杂度是 O(1)(常数时间)。
“那我是不是应该总是使用集合或字典?”
本质上,如果你不需要存储重复元素,那么集合一定比列表更好。就这么简单。
总结
如果你和我一样是个数据性能爱好者,可以看看下面这张关于元组、列表和集合在迭代或检查元素是否存在时的速度对比图。
主要结论如下:
- 如果你需要存储重复元素,请选择列表或元组。
- 在列表和元组之间做选择时,请考虑可变性:如果你需要不可变性,就选择元组。
- 如果你不需要存储重复元素,始终优先选择集合或字典。哈希映射在判断某个对象是否存在于集合(或字典)中时,速度显著更快(例如
x in set_or_dict)。