Python的集合
集合的概念和特点
集合(set)是一个无序的不重复元素序列,集合中的元素不会重复,并且可以进行交集、并集、差集等常见的集合操作
集合的构成由大括号{}包围和使用,分隔,或者使用set()创建集合
set1 = {1,2,3,4,5,5}
set2 = {'1','2','3','4','5'}
set3 = set()
print(set1)
print(set2)
print(type(set3))
输出:
{1, 2, 3, 4, 5}
{'3', '5', '2', '1', '4'}
<class 'set'>
要注意的是set1输出的看起来是顺序输出的,但是其实是因为范围太小,导致输出是按顺序的
原因如下:
- 1.hash(n) = n(整数哈希值等于自身)
- 2.范围小(比如1-5)时,这些哈希值刚好映射到哈希表的连续槽位
- 3.哈希表遍历时按槽位顺序走,所以看起来是 1,2,3,4,5
对小整数来说:
hash(1) == 1
hash(2) == 2
hash(7) == 7
hash(8) == 8
hash(10) == 10
set内部会:
- 根据 hash(x) 计算桶位置
- 小整数 hash 分布非常规整
- 遍历 set 时,刚好按桶顺序输出
所以看起来像排好序了,实际上这是运气好
而当数字变大时:
hash(200) != 200 # hash(200) -> 可能是 200 % N
hash(1000) != 1000 # hash(1000) -> 可能是 1000 % N
哈希表内的: - 哈希表容量 - 冲突 - 扩容
元素会被放进完全不同的位置,遍历时顺序自然就乱了
还有值得一提的是如果输出的集合内是字符类型的话,结果又不一样了,如果在同一个进程内,输出同一个包含字符的集合,输出的顺序是乱序的,根据输出的次数,输出的每一次都是一样的,但是如果重启python或者重启进程,再次运行这个输出,顺序也是乱序的,但是和上一个进程输出的顺序不同,也就随机顺序输出
可以得出一下结论:
| 特性 | 整数 | 字符串 |
|---|---|---|
| 哈希值计算 | hash(n) = n |
基于内容计算 |
| 同进程内顺序 | 固定 | 固定 |
| 跨进程/重启后 | 永远相同(1,2,3...) | 可能变化(依赖哈希随机化种子) |
| 是否"看起来有序" | 小范围时巧合有序 | 几乎看起来乱序 |
输出一个空集合,就是像上面演示的一样,使用set()函数创建空集合,并且python为了区分空集合和空字典,把空集合表现为set(),空字典则是{}
set1 = {1,2,3}
set1.clear()
print(set1)
dict1 = {1:'1',2:'2',3:'3'}
dict1.clear()
print(dict1)
输出:
set()
{}
集合的基础概念就是这些,下面是一些集合的函数和方法
集合的函数和方法
函数
| 函数 | 说明 | 示例 |
|---|---|---|
len(s) |
返回集合元素个数 | len({1, 2, 3}) → 3 |
max(s) |
返回集合中最大值 | max({1, 5, 3}) → 5 |
min(s) |
返回集合中最小值 | min({1, 5, 3}) → 1 |
sum(s) |
返回集合中所有数字之和 | sum({1, 2, 3}) → 6 |
sorted(s) |
返回排序后的列表 | sorted({3, 1, 2}) → [1, 2, 3] |
any(s) |
任一元素为真则返回 True |
any({0, 1}) → True |
all(s) |
所有元素为真则返回 True |
all({1, 2, 3}) → True |
enumerate(s) |
返回枚举对象 | list(enumerate({'a', 'b'})) |
filter(func, s) |
按条件过滤元素 | set(filter(lambda x: x>2, {1,2,3})) |
map(func, s) |
对每个元素执行函数 | set(map(str, {1, 2})) |
方法
| 方法 | 说明 | 示例 |
|---|---|---|
s.add(x) |
添加元素 x |
s.add(4) |
s.update(iterable) |
批量添加元素 | s.update([4, 5]) |
s.remove(x) |
删除元素 x,不存在则报错 |
s.remove(2) |
s.discard(x) |
删除元素 x,不存在不报错 |
s.discard(2) |
s.pop() |
随机删除并返回一个元素 | x = s.pop() |
s.clear() |
清空集合 | s.clear() |
s.copy() |
返回集合的浅拷贝 | s2 = s.copy() |
s.union(other) |
返回并集 | s.union({4, 5}) |
s.intersection(other) |
返回交集 | s.intersection({2, 3}) |
s.difference(other) |
返回差集 | s.difference({2}) |
s.symmetric_difference(other) |
返回对称差集 | s.symmetric_difference({2, 4}) |
s.issubset(other) |
判断是否为子集 | s.issubset({1,2,3}) |
s.issuperset(other) |
判断是否为超集 | s.issuperset({1, 2}) |
s.isdisjoint(other) |
判断两集合是否不相交 | s.isdisjoint({4, 5}) |
s.intersection_update(other) |
更新为交集(原地修改) | s.intersection_update({2,3}) |
s.difference_update(other) |
更新为差集(原地修改) | s.difference_update({2}) |
s.symmetric_difference_update(other) |
更新为对称差集(原地修改) | s.symmetric_difference_update({2,4}) |
还有集合中的特殊用法:
set1 = {1,2,3,4,5}
set2 = {4,5,6,7,8}
# 返回一个包含所有元素的新集
print(f"并集是:{set1 | set2}")
# 返回一个包含共有元素的新集
print(f"交集是:{set1 & set2}")
# 返回一个只出现在set1里的新集
print(f"set1相对set2的差集:{set1 - set2}")
# 询问set1元素是否都在set2中
print(f"set1是否都在set2中:{set1 <= set2}")
输出:
并集是:{1, 2, 3, 4, 5, 6, 7, 8}
交集是:{4, 5}
set1相对set2的差集:{1, 2, 3}
set1是否都在set2中:False