【Python】元组/列表/集合的区别
1.说明
元组(tuple)、列表(list)和集合(set)是Python中常见的数据结构,它们有一些重要的区别。
可变性:列表是可变的(mutable),可以对其进行添加、删除和修改操作。元组是不可变的(immutable),一旦创建就无法修改。集合也是可变的,但是它具有去重功能,不允许存在重复的元素,并且没有固定的顺序。
语法:列表使用方括号
[]
来表示,元素之间用逗号,
分隔;元组使用圆括号()``来表示,元素之间同样用逗号
,分隔;集合使用大括号
{}来表示,元素之间也用逗号
,`分隔。例如:
1
2
3my_list = [1, 2, 3]
my_tuple = (4, 5, 6)
my_set = {7, 8, 9}索引和切片:列表和元组都支持通过索引访问元素和切片操作。而集合是无序的,不支持索引和切片操作。
重复元素:列表和元组允许存在重复的元素,而集合会自动去除重复的元素。
例如:
1
2
3
4my_list = [1, 1, 2, 2, 3, 3] # 列表中可以有重复元素
my_tuple = (4, 4, 5, 5) # 元组中也可以有重复元素
my_set = {6, 6, 7, 7} # 集合会自动去除重复的元素
# 如上my_set = {6,7}可哈希性:集合的元素必须是可哈希(hashable)的,而列表和元组的元素可以是可哈希或不可哈希的。可哈希的意思是该对象的值在其生命周期中不发生改变,且能够唯一地确定一个对象。
总的来说
- 元组适用于存储固定的、不可变的数据;
- 列表适用于需要频繁对其中的元素进行增删改操作的情况;
- 集合适用于需要确保元素唯一性且不关心元素的顺序的场景。
选择使用哪种数据结构取决于具体的需求和问题的特点。
2.可哈希性
2.1 概念
可哈希性(hashability)是指一个对象是否具有哈希值(hash value),并且能够保持不变。在Python中,可哈希的对象是指那些在其生命周期中不可变的对象。
哈希值是一个固定长度的整数,用于唯一标识一个对象。哈希值是通过将对象的内容转换为一个数字来计算得到的。可哈希的对象具有以下特点:
- 哈希值不会改变:一个对象的哈希值在其生命周期中是不变的,即使对象的内容发生了改变。
- 相等的对象具有相同的哈希值:如果两个对象是相等的,则它们的哈希值也相等。
可哈希性在Python中非常重要,主要体现在两个方面:
- 字典的键(key)必须是可哈希的对象:因为字典是基于哈希表实现的,它使用键的哈希值来索引和快速查找对应的值。所以字典中的键必须是不可变的对象,例如整数、浮点数、字符串、元组等都是可哈希的。
- 集合的元素必须是可哈希的对象:集合也是基于哈希表实现的,它使用哈希值来确定元素是否存在于集合中,并且保证集合中的元素唯一性。所以集合中的元素也必须是不可变的对象。
总结起来,可哈希性是指对象在其生命周期中不发生改变,并且能够通过哈希函数计算得到一个固定的、唯一的哈希值。可哈希的对象可以作为字典的键和集合的元素使用。
2.2 自定义类型的可哈希性
自定义类型(Custom types)可以维护可哈希性,但需要满足一定的条件。
在Python中,对象的可哈希性是由其所属类的__hash__()
方法和__eq__()
方法共同决定的。下面是关于自定义类型维护可哈希性的条件:
__hash__()
方法的实现:自定义类型必须定义__hash__()
方法,该方法返回一个整数作为对象的哈希值。通常情况下,可使用内置函数hash()
来计算哈希值,具体实现如下:1
2def __hash__(self):
return hash((self.attribute1, self.attribute2, ...))注意,
__hash__()
方法应该返回一个不可变的值,并且相等的对象应该具有相等的哈希值。__eq__()
方法的实现:为了确保相等的对象具有相等的哈希值,自定义类型也必须定义__eq__()
方法来比较两个对象是否相等。__eq__()
方法通常与__hash__()
方法配合使用,具体实现如下:1
2
3
4def __eq__(self, other):
if isinstance(other, self.__class__):
return (self.attribute1 == other.attribute1) and (self.attribute2 == other.attribute2) and ...
return False注意,
__eq__()
方法应该返回布尔值表示两个对象是否相等。
通过正确实现__hash__()
和__eq__()
方法,自定义类型就可以维护可哈希性。这样,对象就可以作为字典的键或集合的元素,并能够保持不变性和相等性的判断。
然而,请注意,如果自定义类型中的属性是可变的(例如列表、集合等),则对象可能会发生改变,导致哈希值的改变。因此,在定义可哈希的自定义类型时,应该避免使用可变的属性。
2.2.1 hash(自定义类型)
当我们使用 hash
函数来处理自定义类型时,python就会调用这个自定义类型的 __hash__()
函数
1 | hash(p1) |
默认情况下,__hash__
方法返回对象的标识符,而 __eq__
方法在两个对象相同时返回 True。如果想要覆盖这个默认行为,我们可以实现__hash__
方法和 __eq__
方法。
2.2.2 只实现eq
如果一个自定义类型只实现了 __eq__
而没有实现 __hash__
,那么这个类型就不具备可哈希性,也就不能作为dict或者集和的键值
比如如下person对象只实现了 __eq__
1 | class Person: |
如果尝试将这个对象放入set,就会报错
1 | members = { |
1 | TypeError: unhashable type: 'Person' |
同时person对象失去了hash功能
1 | hash(Person('John', 22)) |
为了使得 Person 类可哈希,我们还需要实现 __hash__
方法
1 | class Person: |
现在,Person 类既支持基于 age 的等值比较,又具有哈希功能。
为了使得 Person 能够正常用于字典这种数据结构,类的哈希值必须具有不可变性。为此,我们可以将 age 定义为只读属性
1 | class Person: |
2.2.3 总结
- 默认情况下,
__hash__
方法返回对象的 ID,__eq__
方法使用 is 操作符进行比较。 - 如果实现了
__eq__
方法,Python 会将__hash__
方法设置为 None,除非实现了自定义的__hash__
方法。