查看全课大纲(第 24 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 51.Counter 如何快速统计元素出现次数?

集合(set)怎么用?如何快速去重和做集合运算?

约 22 分钟

小象实战讲义 · Python 编程从入门到实践

你已经学习了列表和元组这两种有序序列。这一节,我们将学习 Python 中的另一种重要数据结构——集合(set)。集合的核心特点是无序和元素不重复,这使得它在数据去重、成员关系判断以及集合运算(如交集、并集)等场景下非常合适。学完本节,你将掌握如何创建、操作集合,并利用其特性解决实际问题。

💡 核心导读

  • 集合是什么:无序、元素不重复的容器,可存储不同类型的数据。
  • 如何创建集合:使用花括号 {} 或 set() 函数,特别注意空集合的创建方法。
  • 核心操作:添加元素(add、update)、删除元素(remove、discard、pop、clear)、判断成员关系(in、not in)。
  • 集合运算:交集(&)、并集(|)、差集(-)、对称差集(^),以及前三种运算的方法写法。
  • 典型应用:利用集合对列表、元组等序列进行快速去重。

集合的定义与特点

集合(set)是 Python 中一种无序存储不重复元素的容器。同一个集合内可以存储不同数据类型的元素。

它与列表、元组的主要区别有两点:

  1. 无序:集合中的元素没有固定的顺序,因此不能通过下标(索引)来访问特定位置的元素。
  2. 元素唯一:集合会自动确保其中的每个元素都是唯一的,重复的元素会被自动过滤掉。

因为无序,直接打印集合时元素的排列顺序并不固定。本节示例统一用 sorted() 排好序再打印,方便对照;集合里同时有字符串和整数时,用 key=str 按字符串形式排序。

# 定义一个集合,包含字符串和一个整数
student_set = {"xiaoming", "xiaoqiang", "xiaobai", "lisi", 13}
print(type(student_set))
print(len(student_set))                 # 使用 len() 获取元素个数
print(sorted(student_set, key=str))     # 排序后打印,便于查看内容
<class 'set'>
5
[13, 'lisi', 'xiaobai', 'xiaoming', 'xiaoqiang']

创建集合的几种方式

1. 使用花括号 {}

这是最直接的方式,用逗号分隔元素,例如 fruits = {"apple", "banana", "orange"}。

2. 使用 set() 函数从其他序列转换

set() 函数可以将列表、元组、字符串等转换为集合,并自动去重。这是最常用的去重方法。需要注意:如果传入的是字符串,会把字符串按字符拆开,每个字符作为独立元素。

3. 创建空集合

特别注意:不能使用 {} 来创建空集合,因为 {} 在 Python 中表示一个空字典。创建空集合必须使用 set() 函数。

# 从列表创建集合(去重)
id_list = ["id1", "id2", "id3", "id1", "id2"]
distinct_set_from_list = set(id_list)
print("从列表创建的集合:", sorted(distinct_set_from_list))

# 从元组创建集合(去重)
id_tuple = ("id1", "id2", "id3", "id1", "id2")
distinct_set_from_tuple = set(id_tuple)
print("从元组创建的集合:", sorted(distinct_set_from_tuple))

# 从字符串创建集合:每个字符成为独立元素,并去重
string_set = set("hello")   # 'l' 出现两次,集合中只保留一个
print("从字符串创建的集合:", sorted(string_set))

# 空字典与空集合
empty_dict = {}
empty_set = set()
print(type(empty_dict))
print(type(empty_set))
print(empty_set)
从列表创建的集合: ['id1', 'id2', 'id3']
从元组创建的集合: ['id1', 'id2', 'id3']
从字符串创建的集合: ['e', 'h', 'l', 'o']
<class 'dict'>
<class 'set'>
set()

集合的基本操作

判断元素是否存在

虽然不能按索引访问,但可以用 in 和 not in 关键字快速判断一个元素是否在集合中,判断结果是 True 或 False。

添加元素

  • add(element):向集合中添加单个元素。如果元素已存在,则无任何效果。
  • update(序列):将一个序列(如列表、元组、另一个集合、字符串)中的所有元素添加到集合中,同时去重。对于字符串,会将其拆分为单个字符添加。

删除元素

集合提供了几种删除元素的方法,它们的关键区别在于处理“元素不存在”时的行为。

方法作用元素不存在时的行为
remove(element)删除指定元素。抛出 KeyError 异常。
discard(element)删除指定元素。静默忽略,不报错。
pop()随机删除并返回一个元素。—
clear()清空集合,使其变为空集合。总是成功。

如何选择?

  • 当你确定元素一定在集合中,或者希望元素不存在时能明确感知到错误,使用 remove。
  • 当你只是想尝试删除,元素不存在也没关系,使用 discard。
  • 当你只关心集合中还剩多少元素、不关心删掉的是哪一个,可以使用 pop。

下面把判断、添加、删除放在同一个可运行示例里,方便对照输出:

distinct_set = set(["id1", "id2", "id3"])
print("当前集合:", sorted(distinct_set))

# 使用 in / not in 判断元素是否存在
user_id = "id1"
if user_id in distinct_set:
    print("{} 存在".format(user_id))

user_id = "id5"
if user_id not in distinct_set:
    print("{} 不存在".format(user_id))

# 添加元素
name_set = {"zhangsan", "lisi"}
name_set.add("wangwu")
print("add 之后:", sorted(name_set))

name_set.update(["悟空", "八戒"])
print("update 列表之后:", sorted(name_set))

name_set.update(("白龙马", "小燕子", "八戒"))   # '八戒'已存在,不会重复添加
print("update 元组之后:", sorted(name_set))

name_set.update({"张飞", "李逵"})              # 合并另一个集合
print("update 集合之后:", sorted(name_set))

name_set.update("沙和尚")                      # 字符串会被拆成单个字符
print("update 字符串之后:", sorted(name_set))

# 删除元素
name_set2 = {"悟空", "八戒", "沙僧"}
name_set2.remove("悟空")   # 元素存在,成功删除
print("remove('悟空')后:", sorted(name_set2))
# name_set2.remove("西游记")  # 如果取消注释,会抛出 KeyError

ret1 = name_set2.discard("沙僧")
print("discard('沙僧')后:", sorted(name_set2))
print("discard 返回值:", ret1)          # discard 无论成功与否都返回 None

ret2 = name_set2.discard("悟空")        # 元素已不存在,静默忽略
print("discard('悟空')后(元素已不存在):", sorted(name_set2))
print("discard 返回值:", ret2)

item = name_set2.pop()                  # 删除并返回一个元素
print("pop() 删除了:", item)
print("pop() 后:", sorted(name_set2))

name_set2.clear()
print("clear() 后:", name_set2)
当前集合: ['id1', 'id2', 'id3']
id1 存在
id5 不存在
add 之后: ['lisi', 'wangwu', 'zhangsan']
update 列表之后: ['lisi', 'wangwu', 'zhangsan', '八戒', '悟空']
update 元组之后: ['lisi', 'wangwu', 'zhangsan', '八戒', '小燕子', '悟空', '白龙马']
update 集合之后: ['lisi', 'wangwu', 'zhangsan', '八戒', '小燕子', '张飞', '悟空', '李逵', '白龙马']
update 字符串之后: ['lisi', 'wangwu', 'zhangsan', '八戒', '和', '小燕子', '尚', '张飞', '悟空', '李逵', '沙', '白龙马']
remove('悟空')后: ['八戒', '沙僧']
discard('沙僧')后: ['八戒']
discard 返回值: None
discard('悟空')后(元素已不存在): ['八戒']
discard 返回值: None
pop() 删除了: 八戒
pop() 后: []
clear() 后: set()

集合的运算

集合支持常见的集合运算,非常直观:交集取两个集合共有的元素;并集取两个集合的全部元素(去重);差集取只在被减集合中存在的元素;对称差集取只在其一集合中出现的元素。交集、并集、差集都同时给出运算符和对应方法两种写法。

set_a = {1, 2, 4, 7}
set_b = {2, 5, 8, 9}

# 1. 交集:两个集合共有的元素
print("交集(&):", sorted(set_a & set_b))
print("交集(intersection):", sorted(set_a.intersection(set_b)))

# 2. 并集:两个集合所有的元素(去重)
print("并集(|):", sorted(set_a | set_b))
print("并集(union):", sorted(set_a.union(set_b)))

# 3. 差集:只在 set_a 中、不在 set_b 中的元素
print("差集(-):", sorted(set_a - set_b))
print("差集(difference):", sorted(set_a.difference(set_b)))

# 4. 对称差集:只在其中一个集合中出现的元素
print("对称差集(^):", sorted(set_a ^ set_b))
交集(&): [2]
交集(intersection): [2]
并集(|): [1, 2, 4, 5, 7, 8, 9]
并集(union): [1, 2, 4, 5, 7, 8, 9]
差集(-): [1, 4, 7]
差集(difference): [1, 4, 7]
对称差集(^): [1, 4, 5, 7, 8, 9]

📝 动手练一练

  1. 你有一个记录网站用户访问日期的列表,其中包含大量重复日期。请使用集合快速找出网站有哪些不同的访问日期。
  2. 有两个学习小组,A组有成员 ['张三', '李四', '王五', '赵六'],B组有成员 ['王五', '赵六', '孙七', '周八']。请找出: a) 同时参加了两个小组的成员。 b) 所有参与了至少一个小组的成员。 c) 只参加了A组但没有参加B组的成员。
👉 点击查看参考答案

思路:

  1. 直接将列表转换为集合即可去重,再用 sorted() 让结果有序、便于查看。
  2. 利用集合运算:交集求共同成员,并集求所有成员,差集求仅属于A组的成员。
# 1. 访问日期去重
visit_dates = ["2023-10-01", "2023-10-02", "2023-10-01", "2023-10-03", "2023-10-02"]
unique_dates = sorted(set(visit_dates))
print("不同的访问日期有: {}".format(unique_dates))

# 2. 学习小组分析
group_a = set(['张三', '李四', '王五', '赵六'])
group_b = set(['王五', '赵六', '孙七', '周八'])

# a) 同时参加两个小组的成员(交集)
print("同时参加两个小组的成员: {}".format(sorted(group_a & group_b)))

# b) 所有参与的成员(并集)
print("所有参与的成员: {}".format(sorted(group_a | group_b)))

# c) 只参加A组的成员(差集)
print("只参加A组的成员: {}".format(sorted(group_a - group_b)))
不同的访问日期有: ['2023-10-01', '2023-10-02', '2023-10-03']
同时参加两个小组的成员: ['王五', '赵六']
所有参与的成员: ['周八', '孙七', '张三', '李四', '王五', '赵六']
只参加A组的成员: ['张三', '李四']

常见错误

错误写法现象或报错原因正确写法
empty_set = {}type(empty_set) 结果是 dict{} 在 Python 中默认表示空字典。empty_set = set()
my_set.remove('不存在的元素')KeyError: '不存在的元素'remove() 要求元素必须存在,否则报错。先使用 in 判断,或改用 discard()。
试图用下标访问 my_set[0]TypeError: 'set' object is not subscriptable集合是无序的,不支持索引访问。用 in 判断成员关系,或先用 sorted() 转成有序列表再按下标取。
name_set.update("沙和尚") 以为会添加一个元素集合里多出“沙”“和”“尚”三个元素update 接收的是序列,字符串会被拆成单个字符逐个添加。需要整体添加一个字符串时用 add("沙和尚")。

小结

  1. 集合(set) 是一种无序、元素不重复的容器,常用于快速去重和成员关系判断。
  2. 创建集合使用花括号 {} 或 set() 函数。创建空集合必须用 set()。
  3. 使用 in 和 not in 判断元素是否在集合中。
  4. 添加元素:add() 用于添加单个元素;update() 用于添加一个序列中的所有元素(字符串会被拆成字符)。
  5. 删除元素:根据需求选择 remove()(严格)、discard()(宽松)、pop()(返回被删元素)或 clear()(清空)。
  6. 集合运算:交集(&)、并集(|)、差集(-)、对称差集(^);前三种还可以用 intersection()、union()、difference() 方法完成。

📋 行动清单

  • 尝试将一个包含重复项的列表转换为集合,观察去重效果。
  • 分别使用 remove('不存在') 和 discard('不存在'),体会两者的区别。
  • 创建两个集合,分别练习使用运算符和方法进行交集、并集、差集的计算。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第3章代码包:集合(含1 个 Notebook)
    ZIP · 2KB
    下载