CodeRoadMap
路线图学习路径文章题库资源社区

浏览

首页路线图学习路径知识库题库文章资源社区我的学习
CodeRoadMap

中文编程学习导航:路线图、讲义与题库,进度可同步。

路线图学习路径文章题库社区

© 2026 CodeRoadMap

津ICP备2026012044号-1|coderoadmap@126.com
首页/Python 100 天/12.常用数据结构之集合

课程目录

  1. 0101.初识Python
  2. 0202.第一个Python程序
  3. 0303.Python语言中的变量
  4. 0404.Python语言中的运算符
  5. 0505.分支结构
  6. 0606.循环结构
  7. 0707.分支和循环结构实战
  8. 0808.常用数据结构之列表-1
  9. 0909.常用数据结构之列表-2
  10. 1010.常用数据结构之元组
  11. 1111.常用数据结构之字符串
  12. 1212.常用数据结构之集合
  13. 1313.常用数据结构之字典
  14. 1414.函数和模块
  15. 1515.函数应用实战
  16. 1616.函数使用进阶
  17. 1717.函数高级应用
  18. 1818.面向对象编程入门
  19. 1919.面向对象编程进阶
  20. 2020.面向对象编程应用
  21. 2121.文件读写和异常处理
  22. 2222.对象的序列化和反序列化
  23. 2323.Python读写CSV文件
  24. 2424.Python读写Excel文件-1
  25. 2525.Python读写Excel文件-2
  26. 2626.Python操作Word和PowerPoint文件
  27. 2727.Python操作PDF文件
  28. 2828.Python处理图像
  29. 2929.Python发送邮件和短信
  30. 3030.正则表达式的应用
  31. 3131.Python语言进阶
  32. 3232-33.Web前端入门
  33. 3334-35.玩转Linux操作系统
  34. 3436.关系型数据库和MySQL概述
  35. 3537.SQL详解之DDL
  36. 3638.SQL详解之DML
  37. 3739.SQL详解之DQL
  38. 3840.SQL详解之DCL
  39. 3941.MySQL新特性
  40. 4042.视图、函数和过程
  41. 4143.索引
  42. 4244.Python接入MySQL数据库
  43. 4345.Hive实战
  44. 4446.Django快速上手
  45. 4547.深入模型
  46. 4648.静态资源和Ajax请求
  47. 4749.Cookie和Session
  48. 4850.制作报表
  49. 4951.日志和调试工具栏
  50. 5052.中间件的应用
  51. 5153.前后端分离开发入门
  52. 5254.RESTful架构和DRF入门
  53. 5355.RESTful架构和DRF进阶
  54. 5456.使用缓存
  55. 5557.接入三方平台
  56. 5658.异步任务和定时任务
  57. 5759.单元测试
  58. 5860.项目上线
  59. 5961.网络数据采集概述
  60. 6062.用Python获取网络资源-1
  61. 6162.用Python解析HTML页面-2
  62. 6263.并发编程在爬虫中的应用
  63. 6363.Python中的并发编程-1
  64. 6463.Python中的并发编程-2
  65. 6563.Python中的并发编程-3
  66. 6664.使用Selenium抓取网页动态内容
  67. 6765.爬虫框架Scrapy简介
  68. 6866.数据分析概述
  69. 6967.环境准备
  70. 7068.NumPy的应用-1
  71. 7169.NumPy的应用-2
  72. 7270.NumPy的应用-3
  73. 7371.NumPy的应用-4
  74. 7472.深入浅出pandas-1
  75. 7573.深入浅出pandas-2
  76. 7674.深入浅出pandas-3
  77. 7775.深入浅出pandas-4
  78. 7876.深入浅出pandas-5
  79. 7977.深入浅出pandas-6
  80. 8078.数据可视化-1
  81. 8179.数据可视化-2
  82. 8280.数据可视化-3
  83. 8381.浅谈机器学习
  84. 8482.k最近邻算法
  85. 8583.决策树和随机森林
  86. 8684.朴素贝叶斯算法
  87. 8785.回归模型
  88. 8886.K-Means聚类算法
  89. 8987.集成学习算法
  90. 9088.神经网络模型
  91. 9189.自然语言处理入门
  92. 9290.机器学习实战
  93. 9391.团队项目开发的问题和解决方案
  94. 9492.Docker容器技术详解
  95. 9593.MySQL性能优化
  96. 9694.网络API接口设计
  97. 9795.使用Django开发商业项目
  98. 9896.软件测试和自动化测试
  99. 9997.电商网站技术要点剖析
  100. 10098.项目部署上线和性能调优
  101. 10199.面试中的公共问题
  102. 102100.补充内容
第 12 课1 天

12.常用数据结构之集合

在学习了列表和元组之后,我们再来学习一种容器型的数据类型,它的名字叫集合(set)。说到集合这个词大家一定不会陌生,在数学课本上就有这个概念。如果我们把一定范围

课程内容

常用数据结构之集合

在学习了列表和元组之后,我们再来学习一种容器型的数据类型,它的名字叫集合(set)。说到集合这个词大家一定不会陌生,在数学课本上就有这个概念。如果我们把一定范围的、确定的、可以区别的事物当作一个整体来看待,那么这个整体就是集合,集合中的各个事物称为集合的元素。通常,集合需要满足以下要求:

  1. 无序性:一个集合中,每个元素的地位都是相同的,元素之间是无序的。
  2. 互异性:一个集合中,任何两个元素都是不相同的,即元素在集合中只能出现一次。
  3. 确定性:给定一个集合和一个任意元素,该元素要么属这个集合,要么不属于这个集合,二者必居其一,不允许有模棱两可的情况出现。

Python 程序中的集合跟数学上的集合没有什么本质区别,需要强调的是上面所说的无序性和互异性。无序性说明集合中的元素并不像列中的元素那样存在某种次序,可以通过索引运算就能访问任意元素,集合并不支持索引运算。另外,集合的互异性决定了集合中不能有重复元素,这一点也是集合区别于列表的地方,我们无法将重复的元素添加到一个集合中。集合类型必然是支持in和not in成员运算的,这样就可以确定一个元素是否属于集合,也就是上面所说的集合的确定性。集合的成员运算在性能上要优于列表的成员运算,这是集合的底层存储特性决定的,此处我们暂时不做讨论,大家记住这个结论即可。

说明:集合底层使用了哈希存储(散列存储),对哈希存储不了解的读者可以先看看“Hello 算法”网站对哈希表的讲解,感谢作者的开源精神。

创建集合

在 Python 中,创建集合可以使用{}字面量语法,{}中需要至少有一个元素,因为没有元素的{}并不是空集合而是一个空字典,字典类型我们会在下一节课中为大家介绍。当然,也可以使用 Python 内置函数set来创建一个集合,准确的说set并不是一个函数,而是创建集合对象的构造器,这个知识点会在后面讲解面向对象编程的地方为大家介绍。我们可以使用set函数创建一个空集合,也可以用它将其他序列转换成集合,例如:set('hello')会得到一个包含了4个字符的集合(重复的字符l只会在集合中出现一次)。除了这两种方式,还可以使用生成式语法来创建集合,就像我们之前用生成式语法创建列表那样。

set1 = {1, 2, 3, 3, 3, 2}
print(set1)

set2 = {'banana', 'pitaya', 'apple', 'apple', 'banana', 'grape'}
print(set2)

set3 = set('hello')
print(set3)

set4 = set([1, 2, 2, 3, 3, 3, 2, 1])
print(set4)

set5 = {num for num in range(1, 20) if num % 3 == 0 or num % 7 == 0}
print(set5)

需要提醒大家,集合中的元素必须是hashable类型,所谓hashable类型指的是能够计算出哈希码的数据类型,通常不可变类型都是hashable类型,如整数(int)、浮点小数(float)、布尔值(bool)、字符串(str)、元组(tuple)等。可变类型都不是hashable类型,因为可变类型无法计算出确定的哈希码,所以它们不能放到集合中。例如:我们不能将列表作为集合中的元素;同理,由于集合本身也是可变类型,所以集合也不能作为集合中的元素。我们可以创建出嵌套列表(列表的元素也是列表),但是我们不能创建出嵌套的集合,这一点在使用集合的时候一定要引起注意。

温馨提示:如果不理解上面提到的哈希码、哈希存储这些概念,可以先放放,因为它并不影响你继续学习和使用 Python 语言。当然,如果是计算机专业的小伙伴,不理解哈希存储是很难被原谅的,要赶紧去补课了。

元素的遍历

我们可以通过len函数来获得集合中有多少个元素,但是我们不能通过索引运算来遍历集合中的元素,因为集合元素并没有特定的顺序。当然,要实现对集合元素的遍历,我们仍然可以使用for-in循环,代码如下所示。

set1 = {'Python', 'C++', 'Java', 'Kotlin', 'Swift'}
for elem in set1:
    print(elem)

提示:大家看看上面代码的运行结果,通过单词输出的顺序体会一下集合的无序性。

集合的运算

Python 为集合类型提供了非常丰富的运算,主要包括:成员运算、交集运算、并集运算、差集运算、比较运算(相等性、子集、超集)等。

成员运算

可以通过成员运算in和not in 检查元素是否在集合中,代码如下所示。

set1 = {11, 12, 13, 14, 15}
print(10 in set1)      # False 
print(15 in set1)      # True
set2 = {'Python', 'Java', 'C++', 'Swift'}
print('Ruby' in set2)  # False
print('Java' in set2)  # True

二元运算

集合的二元运算主要指集合的交集、并集、差集、对称差等运算,这些运算可以通过运算符来实现,也可以通过集合类型的方法来实现,代码如下所示。

set1 = {1, 2, 3, 4, 5, 6, 7}
set2 = {2, 4, 6, 8, 10}

# 交集
print(set1 & set2)                      # {2, 4, 6}
print(set1.intersection(set2))          # {2, 4, 6}

# 并集
print(set1 | set2)                      # {1, 2, 3, 4, 5, 6, 7, 8, 10}
print(set1.union(set2))                 # {1, 2, 3, 4, 5, 6, 7, 8, 10}

# 差集
print(set1 - set2)                      # {1, 3, 5, 7}
print(set1.difference(set2))            # {1, 3, 5, 7}

# 对称差
print(set1 ^ set2)                      # {1, 3, 5, 7, 8, 10}
print(set1.symmetric_difference(set2))  # {1, 3, 5, 7, 8, 10}

通过上面的代码可以看出,对两个集合求交集,&运算符和intersection方法的作用是完全相同的,使用运算符的方式显然更直观且代码也更简短。需要说明的是,集合的二元运算还可以跟赋值运算一起构成复合赋值运算,例如:set1 |= set2相当于set1 = set1 | set2,跟|=作用相同的方法是update;set1 &= set2相当于set1 = set1 & set2,跟&=作用相同的方法是intersection_update,代码如下所示。

set1 = {1, 3, 5, 7}
set2 = {2, 4, 6}
set1 |= set2
# set1.update(set2)
print(set1)  # {1, 2, 3, 4, 5, 6, 7}
set3 = {3, 6, 9}
set1 &= set3
# set1.intersection_update(set3)
print(set1)  # {3, 6}
set2 -= set1
# set2.difference_update(set1)
print(set2)  # {2, 4}

比较运算

两个集合可以用==和!=进行相等性判断,如果两个集合中的元素完全相同,那么==比较的结果就是True,否则就是False。如果集合A的任意一个元素都是集合B的元素,那么集合A称为集合B的子集,即对于 $\small{\forall{a} \in {A}}$ ,均有 $\small{{a} \in {B}}$ ,则 $\small{{A} \subseteq {B}}$ ,A是B的子集,反过来也可以称B是A的超集。如果A是B的子集且A不等于B,那么A就是B的真子集。Python 为集合类型提供了判断子集和超集的运算符,其实就是我们非常熟悉的<、<=、>、>=这些运算符。当然,我们也可以通过集合类型的方法issubset和issuperset来判断集合之间的关系,代码如下所示。

set1 = {1, 3, 5}
set2 = {1, 2, 3, 4, 5}
set3 = {5, 4, 3, 2, 1}

print(set1 < set2)   # True
print(set1 <= set2)  # True
print(set2 < set3)   # False
print(set2 <= set3)  # True
print(set2 > set1)   # True
print(set2 == set3)  # True

print(set1.issubset(set2))    # True
print(set2.issuperset(set1))  # True

说明:上面的代码中,set1 < set2判断set1是不是set2的真子集,set1 <= set2判断set1是不是set2的子集,set2 > set1判断set2是不是set1的超集。当然,我们也可以通过set1.issubset(set2)判断set1是不是set2的子集;通过set2.issuperset(set1)判断set2是不是set1的超集。

集合的方法

刚才我们说过,Python 中的集合是可变类型,我们可以通过集合的方法向集合添加元素或从集合中删除元素。

set1 = {1, 10, 100}

# 添加元素
set1.add(1000)
set1.add(10000)
print(set1)  # {1, 100, 1000, 10, 10000}

# 删除元素
set1.discard(10)
if 100 in set1:
    set1.remove(100)
print(set1)  # {1, 1000, 10000}

# 清空元素
set1.clear()
print(set1)  # set()

说明:删除元素的remove方法在元素不存在时会引发KeyError错误,所以上面的代码中我们先通过成员运算判断元素是否在集合中。集合类型还有一个pop方法可以从集合中随机删除一个元素,该方法在删除元素的同时会返回(获得)被删除的元素,而remove和discard方法仅仅是删除元素,不会返回(获得)被删除的元素。

集合类型还有一个名为isdisjoint的方法可以判断两个集合有没有相同的元素,如果没有相同元素,该方法返回True,否则该方法返回False,代码如下所示。

set1 = {'Java', 'Python', 'C++', 'Kotlin'}
set2 = {'Kotlin', 'Swift', 'Java', 'Dart'}
set3 = {'HTML', 'CSS', 'JavaScript'}
print(set1.isdisjoint(set2))  # False
print(set1.isdisjoint(set3))  # True

不可变集合

Python 中还有一种不可变类型的集合,名字叫frozenset。set跟frozenset的区别就如同list跟tuple的区别,frozenset由于是不可变类型,能够计算出哈希码,因此它可以作为set中的元素。除了不能添加和删除元素,frozenset在其他方面跟set是一样的,下面的代码简单的展示了frozenset的用法。

fset1 = frozenset({1, 3, 5, 7})
fset2 = frozenset(range(1, 6))
print(fset1)          # frozenset({1, 3, 5, 7})
print(fset2)          # frozenset({1, 2, 3, 4, 5})
print(fset1 & fset2)  # frozenset({1, 3, 5})
print(fset1 | fset2)  # frozenset({1, 2, 3, 4, 5, 7})
print(fset1 - fset2)  # frozenset({7})
print(fset1 < fset2)  # False

总结

Python 中的集合类型是一种无序容器,不允许有重复运算,由于底层使用了哈希存储,集合中的元素必须是hashable类型。集合与列表最大的区别在于集合中的元素没有顺序、所以不能够通过索引运算访问元素、但是集合可以执行交集、并集、差集等二元运算,也可以通过关系运算符检查两个集合是否存在超集、子集等关系。

← 上一课11.常用数据结构之字符串下一课 →13.常用数据结构之字典