Skip to content

Python 集合 ​

报名名单中可能重复出现同一个姓名。现在有一个问题:如何只保留不重复的姓名,同时接受集合不保证原顺序这一特点?

案例:整理报名名单 ​

需求:将重复的报名姓名整理为不重复的数据。

python
name_list = ["小明", "小红", "小明", "小刚", "小红"]
name_set = set(name_list)

print(name_set)

重复的 "小明" 和 "小红" 只会保留一份。集合不保证元素的位置顺序,因此实际输出顺序可能和原列表不同。

集合和数学中的集合很像,主要解决两个问题:

  1. 数据去重
  2. 计算两组数据的交集、并集、差集

列表转换为集合后自动去重且不保证顺序的示意图

集合创建与转换 ​

非空集合可以使用 {} 创建,元素之间用逗号分隔:

python
course_set = {"Python", "MySQL", "Linux"}
print(course_set)

也可以使用 set() 把列表、元组或字符串转换成集合:

python
number_set = set([1, 2, 2, 3, 3, 3])
char_set = set("hello")

print(number_set)
print(char_set)

集合的显示顺序可能与写入顺序不同,但重复元素一定会被去掉。

空集合创建 ​

空集合必须使用 set() 创建:

python
empty_set = set()
empty_dict = {}

print(type(empty_set))
print(type(empty_dict))

运行结果:

text
<class 'set'>
<class 'dict'>

{} 表示空字典,不是空集合。这是刚开始最容易写错的地方。

集合特性 ​

集合的使用边界可以概括为三个特点:

  1. 元素不重复
  2. 不保证元素的位置顺序
  3. 集合本身可以修改

元素唯一性 ​

创建集合时,重复元素会自动合并:

python
number_set = {1, 2, 2, 3, 3, 3}
print(number_set)

集合适合判断“某个值是否出现过”,但不记录同一个值出现了多少次。

无索引访问 ​

集合不保证元素的位置顺序,因此不能像列表、元组那样按索引取值或切片。

如果需求要求按位置读取,就不能直接使用集合:

python
course_set = {"Python", "MySQL", "Linux"}
print(course_set[0])

运行后会抛出异常:

text
TypeError: 'set' object is not subscriptable

元素类型限制 ​

集合需要判断元素是否已经存在,因此放入集合的元素必须可哈希。初学阶段可以先记住:数字、字符串和只包含不可变数据的元组可以作为集合元素;列表、字典和集合本身不能直接放进集合。

python
position_set = {(10, 20), (30, 40)}
print(position_set)

如果把列表放进集合,会抛出 TypeError: unhashable type: 'list'。

集合修改 ​

报名课程会增加或取消,集合可以修改,但不能按位置操作元素。

元素添加 ​

add() 添加一个元素,update() 批量添加多个元素:

python
course_set = {"Python", "MySQL"}

course_set.add("Linux")
course_set.update(["Redis", "Docker"])

print(course_set)

如果添加的元素已经存在,集合不会重复保存,也不会报错。

元素删除 ​

集合常用的删除操作如下:

操作作用元素不存在时
remove(data)删除指定元素抛出 KeyError
discard(data)删除指定元素不报错
pop()删除并返回一个元素空集合会抛出 KeyError
clear()清空集合不报错
python
course_set = {"Python", "MySQL", "Linux"}

course_set.remove("MySQL")
course_set.discard("Redis")

print(course_set)

这里的 "Redis" 不在集合中,但 discard() 不会报错。如果换成 remove("Redis"),程序会抛出 KeyError。

集合添加、删除和成员判断的示意图

警告

集合不保证元素的位置顺序,因此 pop() 删除的是其中一个元素,不能把它理解成“删除最后一个元素”。

成员判断与遍历 ​

如果只关心某门课程是否报名,使用 in 和 not in 判断元素是否存在:

python
course_set = {"Python", "MySQL", "Linux"}

print("Python" in course_set)
print("Redis" not in course_set)

运行结果:

text
True
True

使用 for 可以遍历集合,但不能依赖它的遍历顺序:

python
course_set = {"Python", "MySQL", "Linux"}

for course in course_set:
    print(course)

如果业务需要按固定顺序处理数据,就不应该只使用集合保存这些数据。

集合运算 ​

两组学员选择了不同的课程时,新的问题是:如何找出共同课程、全部课程,以及只被一组学员选择的课程?

python
group_a = {"Python", "MySQL", "Linux"}
group_b = {"Python", "Redis", "Linux"}

集合可以直接完成四种运算:

运算符号含义
交集&两边都有的元素
并集|合并两边的元素并去重
差集-左边有、右边没有的元素
对称差集^只在其中一边出现的元素
python
group_a = {"Python", "MySQL", "Linux"}
group_b = {"Python", "Redis", "Linux"}

print("交集:", group_a & group_b)
print("并集:", group_a | group_b)
print("差集:", group_a - group_b)
print("对称差集:", group_a ^ group_b)

结果中的元素顺序可能不同,但元素内容如下:

  • 交集:"Python"、"Linux"
  • 并集:"Python"、"MySQL"、"Redis"、"Linux"
  • 差集:"MySQL"
  • 对称差集:"MySQL"、"Redis"

差集有方向。group_a - group_b 和 group_b - group_a 的结果通常不同。

集合交集、并集、差集和对称差集的关系图

案例:比较两个字符串 ​

需求:比较两个字符串中出现的字符,分别找出共有字符、不同字符和 text_a 多出的字符。

  1. 找出两个字符串共有的字符
  2. 找出只在其中一个字符串出现的字符
  3. 找出 text_a 比 text_b 多出的字符
python
text_a = "hello world!"
text_b = "hello python!"

先把两个字符串转换成集合,再做集合运算。转换后只记录字符是否出现,不再记录每个字符出现的次数。

参考答案
python
text_a = "hello world!"
text_b = "hello python!"

char_set_a = set(text_a)
char_set_b = set(text_b)

# 两个字符串共有的字符
print("共有字符:", char_set_a & char_set_b)

# 只在其中一个字符串出现的字符
print("不同字符:", char_set_a ^ char_set_b)

# text_a 有、text_b 没有的字符
print("text_a 多出的字符:", char_set_a - char_set_b)

集合结果中可能包含空格字符。观察结果时,注意不要漏掉显示为 ' ' 的空格。

常见问题 ​

去重后的顺序变化 ​

下面的代码可以去重,但不能保证保留列表原来的顺序:

python
number_list = [3, 1, 2, 3, 2]
number_list = list(set(number_list))

print(number_list)

如果任务要求“去重后仍保持原顺序”,还需要使用其他写法,后面的综合练习会继续处理。

空集合与空字典 ​

{} 创建的是字典。需要空集合时,必须写成 set()。

元素顺序依赖 ​

不要依赖集合的显示顺序,也不要尝试使用索引。需要按位置保存和处理数据时,应使用列表或元组。

小结 ​

本节先回答五个问题:

  1. 什么时候使用集合?需要去重、判断成员或比较多组数据时
  2. 集合有哪些边界?元素不重复、不保证顺序,不能使用索引和切片
  3. 空集合如何创建?必须使用 set(),{} 表示空字典
  4. 如何维护集合?add() 添加一个元素,update() 批量添加,删除时根据是否允许缺失选择 remove() 或 discard()
  5. 如何比较两组数据?使用 &、|、-、^ 分别计算交集、并集、差集和对称差集

练习 ​

  1. 把包含重复数字的列表转换成集合,观察结果
  2. 使用 add()、update()、remove() 和 discard() 修改集合
  3. 定义两个集合,分别计算交集、并集、差集和对称差集
  4. 用集合找出 "hello world" 和 "hello python" 共有的字符