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
返回列表