第5章 组合数据类型及其应用
5.4 集合及其应用

【学习目标】
通过本节学习,你将能够: 1. 理解集合的基本概念和特性 2. 掌握集合的创建方法 3. 熟练运用集合的运算操作 4. 掌握集合的常用函数和方法 5. 利用集合的去重特性解决实际问题

5.4.1 集合概述
一、集合的概念
1. 什么是集合?
集合(Set) 是Python中的一种组合数据类型,它与数学中的集合概念类似,具有以下核心特征:
·无序性:集合中的元素没有固定的顺序,不能通过索引访问
·唯一性:集合中的元素不能重复,每个元素都是唯一的
·可变性:集合是可变的,可以添加或删除元素
2. 集合与前面学过数据类型的对比
让我们回顾一下前面章节学习过的数据类型,通过对比来理解集合的独特之处:
数据类型 | 是否有序 | 是否可重复 | 是否可变 | 访问方式 |
字符串(str) | 有序 | 可重复 | 不可变 | 索引 |
列表(list) | 有序 | 可重复 | 可变 | 索引 |
元组(tuple) | 有序 | 可重复 | 不可变 | 索引 |
集合(set) | 无序 | 不可重复 | 可变 | 无法索引 |
回忆与串联: - 第2章我们学习了字符串,知道字符串是有序的字符序列,可以通过索引访问每个字符 - 第5章前面的小节我们学习了列表和元组,它们都是有序序列 - 现在学习的集合打破了”有序”和”可重复”的传统,这是为了解决特定问题而设计的
3. 集合的应用场景
集合在编程中有许多实用场景:
1.去重:快速去除列表中的重复元素
2.成员检测:快速判断某个元素是否在集合中(比列表更快)
3.数学集合运算:交集、并集、差集等
4.关系判断:判断两个数据集的关系
CK学校案例:假设CK学校数字经济专业要统计选修了”Python编程”和”数据分析”两门课程的学生名单,找出同时选修两门课的学生(交集),或者只选修了一门课的学生(对称差集),这时集合就非常有用。

二、集合的定义
1. 使用花括号创建集合
# 创建一个包含重庆特色小吃的集合chongqing_snacks = {"火锅", "小面", "酸辣粉", "毛血旺", "辣子鸡"}print(chongqing_snacks)print(type(chongqing_snacks))
输出说明: - 集合的输出顺序可能与定义时不同(无序性) - type()函数返回
2. 使用set()函数创建集合
# 从列表创建集合(自动去重)ck_students = ["张三", "李四", "王五", "张三", "李四"]# 有重复unique_students =set(ck_students)print(f"原始列表:{ck_students}")print(f"去重后的集合:{unique_students}")print(f"学生人数:{len(unique_students)}")
知识点串联: - len()函数我们在第2章学习过,用于获取字符串长度,这里同样适用于集合 - 列表允许重复,集合自动去重,这是两者的本质区别
3. 创建空集合
# 错误示范:{}创建的是空字典,不是空集合empty_dict = {}print(type(empty_dict))## 正确方式:使用set()创建空集合empty_set =set()print(type(empty_set))#
重要提醒:空花括号{}在Python中表示空字典,这是需要特别注意的地方!
4. 集合元素的类型要求
集合中的元素必须是不可变类型:
# 合法:数字、字符串、元组valid_set = {1, 2, "hello", (3, 4)}print("合法集合:", valid_set)# 非法:列表、字典、集合(可变类型)# invalid_set = {[1, 2], {"a": 1}}# 这会报错!
回忆第2章和第5章: - 数字(int, float)是不可变的 - 字符串是不可变的 - 元组是不可变的 - 列表、字典是可变的,不能作为集合元素
5. 集合的自动去重特性演示
# 重庆地铁线路站点统计案例line1_stations = ["小什字", "较场口", "七星岗", "两路口", "鹅岭", "大坪", "石油路", "歇台子", "石桥铺", "高庙村", "马家岩", "小龙坎", "沙坪坝", "杨公桥", "烈士墓", "磁器口", "石井坡", "双碑", "赖家桥", "微电园", "陈家桥", "大学城", "尖顶坡", "璧山"]# 假设我们收集的数据中有重复(比如换乘站被记录了多次)collect_data = line1_stations + ["两路口", "大坪", "石桥铺"]# 添加重复数据print(f"收集的原始数据量:{len(collect_data)}")print(f"实际不重复站点数:{len(set(collect_data))}")print(f"去重后的站点:{set(collect_data)}")

5.4.2 集合处理
一、集合的运算
集合支持数学中的集合运算,这是集合最强大的功能之一。
1. 并集运算(Union)
概念:两个集合的所有元素合并在一起,重复元素只保留一个。
运算符:| 或 union()方法
# CK学校社团成员统计案例python_club = {"张三", "李四", "王五", "赵六"}# Python编程社团data_club = {"李四", "王五", "孙七", "周八"}# 数据分析社团# 计算两个社团的所有成员(并集)all_members = python_club | data_club# 或者使用:all_members = python_club.union(data_club)print(f"Python社团成员:{python_club}")print(f"数据社团成员:{data_club}")print(f"两个社团的所有成员(并集):{all_members}")print(f"总人数:{len(all_members)}")
可视化理解:
Python社团数据社团张三孙七李四 ───┬───李四王五 ───┘───王五赵六周八并集结果:{张三, 李四, 王五, 赵六, 孙七, 周八}
2. 交集运算(Intersection)
概念:两个集合共有的元素。
运算符:& 或 intersection()方法
# CK学校社团成员统计案例python_club = {"张三", "李四", "王五", "赵六"}# Python编程社团data_club = {"李四", "王五", "孙七", "周八"}# 数据分析社团# 计算同时参加两个社团的成员(交集)both_clubs = python_club & data_club# 或者使用:both_clubs = python_club.intersection(data_club)print(f"同时参加两个社团的成员(交集):{both_clubs}")# 重庆景点偏好调查案例local_favorite = {"洪崖洞", "解放碑", "磁器口", "长江索道", "李子坝"}tourist_favorite = {"洪崖洞", "长江索道", "李子坝", "武隆", "大足石刻"}popular_spots = local_favorite & tourist_favoriteprint(f"本地人和游客都喜欢的景点:{popular_spots}")
3. 差集运算(Difference)
概念:在第一个集合中但不在第二个集合中的元素。
运算符:- 或 difference()方法
# CK学校社团成员统计案例python_club = {"张三", "李四", "王五", "赵六"}# Python编程社团data_club = {"李四", "王五", "孙七", "周八"}# 数据分析社团# 只参加Python社团但没参加数据社团的成员only_python = python_club - data_club# 或者使用:only_python = python_club.difference(data_club)print(f"只参加Python社团的成员:{only_python}")# 只参加数据社团但没参加Python社团的成员only_data = data_club - python_clubprint(f"只参加数据社团的成员:{only_data}")
注意:差集运算不对称,A - B 不等于 B - A。
4. 对称差集运算(Symmetric Difference)
概念:在两个集合中,但不同时在两个集合中的元素(即只属于其中一个集合的元素)。
运算符:^ 或 symmetric_difference()方法
# CK学校社团成员统计案例python_club = {"张三", "李四", "王五", "赵六"}# Python编程社团data_club = {"李四", "王五", "孙七", "周八"}# 数据分析社团# 只参加了一个社团的成员(对称差集)only_one_club = python_club ^ data_club# 或者使用:only_one_club = python_club.symmetric_difference(data_club)print(f"只参加一个社团的成员:{only_one_club}")# 等价于:(python_club | data_club) - (python_club & data_club)
5. 子集和超集判断
# 子集判断:A是否是B的子集(A的所有元素都在B中)A = {1, 2, 3}B = {1, 2, 3, 4, 5}print(f"A是B的子集吗?{A <= B}")# Trueprint(f"A是B的子集吗?{A.issubset(B)}")# True# 超集判断:B是否是A的超集print(f"B是A的超集吗?{B >= A}")# Trueprint(f"B是A的超集吗?{B.issuperset(A)}")# True# 真子集判断(使用<)print(f"A是B的真子集吗?{A < B}")# True(A是子集且A不等于B)# CK学校课程选修案例python_basic = {"张三", "李四", "王五"}python_advanced = {"张三", "李四"}# 高级班是基本班的子集print(f"高级班学生都在基本班吗?{python_advanced <= python_basic}")
6. 不相交判断
# CK学校社团成员统计案例python_club = {"张三", "李四", "王五", "赵六"}# Python编程社团# 判断两个集合是否有共同元素math_club = {"张三", "赵六"}art_club = {"孙七", "周八"}print(f"数学社团和Python社团没有共同成员吗?{math_club.isdisjoint(python_club)}")print(f"数学社团和艺术社团没有共同成员吗?{math_club.isdisjoint(art_club)}")

二、集合处理函数与方法
1. 添加元素
# 创建空集合ck_events =set()# add()方法:添加单个元素ck_events.add("开学典礼")ck_events.add("运动会")ck_events.add("元旦晚会")print(f"当前活动:{ck_events}")# 添加重复元素(不会报错,但集合不会变化)ck_events.add("运动会")print(f"尝试添加重复元素后:{ck_events}")# 运动会仍然只出现一次# update()方法:添加多个元素(可以是列表、元组、字符串等)new_events = ["科技节", "创业大赛", "毕业典礼"]ck_events.update(new_events)print(f"添加多个活动后:{ck_events}")# 也可以直接添加另一个集合other_events = {"社团招新", "志愿者活动"}ck_events.update(other_events)print(f"合并集合后:{ck_events}")
知识点串联: - add()类似于列表的append(),但列表可以添加重复元素,集合会自动去重 - update()类似于列表的extend(),用于批量添加元素
2. 删除元素
# remove()方法:删除指定元素,元素不存在时报错courses = {"Python", "Java", "C++", "数据结构"}courses.remove("Java")print(f"删除Java后:{courses}")# courses.remove("Go")# 会报错:KeyError# discard()方法:删除指定元素,元素不存在时不报错courses.discard("C++")courses.discard("Go")# 不会报错,即使Go不存在print(f"使用discard后:{courses}")# pop()方法:随机删除并返回一个元素(因为集合无序)removed = courses.pop()print(f"被随机删除的元素:{removed}")print(f"剩余元素:{courses}")# clear()方法:清空集合temp_set = {1, 2, 3}temp_set.clear()print(f"清空后:{temp_set}")# set()
回忆第5章列表操作: - 列表的remove()也是删除指定元素,但列表有顺序,集合没有 - 列表的pop()默认删除最后一个元素,集合的pop()是随机的
3. 集合的遍历
# 虽然集合是无序的,但可以用for循环遍历chongqing_districts = {"渝中", "江北", "南岸", "沙坪坝", "九龙坡", "渝北"}print("重庆主城区:")for district in chongqing_districts:print(f"- {district}")# 使用enumerate()(虽然顺序不固定,但可以给每个元素编号)print("\n带编号的遍历:")for index, district inenumerate(chongqing_districts, 1):print(f"{index}. {district}")
回忆第4章循环: - for循环可以遍历任何可迭代对象 - enumerate()函数我们在第4章学习过,用于同时获取索引和元素
4. 集合推导式
# 基本语法:{表达式 for 变量 in 可迭代对象 if 条件}'''表达式:计算结果会作为集合的元素。变量:从可迭代对象中取出的每个元素。可迭代对象:如列表、元组、字符串、range 等。if 条件(可选):筛选满足条件的元素。'''# 示例1:创建1-20中能被3整除的数的集合multiples_of_3 = {x for x inrange(1, 21) if x %3==0}print(f"1-20中3的倍数:{multiples_of_3}")# 示例2:从字符串集合中提取长度大于2的字符串words = {"Python", "CK", "重庆", "编程", "数字经济", "AI"}long_words = {word for word in words iflen(word) >2}print(f"长度大于2的词:{long_words}")# 示例3:将列表转换为集合并同时处理(去重并转大写)names_list = ["Alice", "Bob", "alice", "bob", "Charlie"]unique_upper_names = {name.upper() for name in names_list}print(f"去重并大写:{unique_upper_names}")
知识点串联: - 集合推导式与列表推导式语法类似,只是用{}代替[]
5. 其他常用方法
# copy():复制集合original = {1, 2, 3}copied = original.copy()print(f"原集合:{original}")print(f"复制品:{copied}")# len():获取集合元素个数(内置函数)print(f"元素个数:{len(original)}")# in / not in:成员检测(比列表更快)print(f"2在集合中吗?{2in original}")print(f"5不在集合中吗?{5notin original}")# 性能对比示例import time# 创建大型数据集large_list =list(range(100000))large_set =set(range(100000))# 测试列表查找target =99999start = time.time()for _ inrange(1000):target in large_listlist_time = time.time() - start# 测试集合查找start = time.time()for _ inrange(1000):target in large_setset_time = time.time() - startprint(f"\n列表查找1000次耗时:{list_time:.4f}秒")print(f"集合查找1000次耗时:{set_time:.4f}秒")print(f"集合比列表快约:{list_time/set_time:.0f}倍")

5.4.3 程序设计实例——CK学校年会抽奖程序
一、问题分析
场景:CK学校经济管理学院年会,需要从800名教职工中随机抽取30名幸运奖获得者。
核心需求: 1. 随机生成1-800之间的号码 2. 确保每个号码只被抽取一次(不能重复中奖) 3. 抽取满30人后停止 4. 输出生成的所有中奖号码
为什么选择集合? - 集合的唯一性天然满足”不重复中奖”的需求 - 集合的无序性与随机抽奖的随机性相契合 - 使用集合可以简化代码逻辑,无需手动检查重复
二、算法设计
算法步骤:1. 创建一个空集合numbers用于存储中奖号码2. 当集合元素个数小于30时,循环执行:a. 生成1-800之间的随机整数b. 将随机数添加到集合numbers中(如果该数已存在,集合自动忽略,不报错)3. 循环结束,输出生成的30个号码4. 将集合转换为列表并排序输出(可选)
三、代码实现
import random# 创建空集合存储中奖号码lucky_numbers =set()# 循环直到集合中有30个元素whilelen(lucky_numbers) <30:# 生成1-800之间的随机数number = random.randint(1, 800)# 添加到集合(自动去重)lucky_numbers.add(number)# 可以显示添加过程(可选)iflen(lucky_numbers) %5==0:print(f"已抽取 {len(lucky_numbers)} 人...")print("\n"+"="*50)print("🎉 CK学校经济管理学院年会抽奖结果 🎉")print("="*50)# 转换为列表并排序,便于查看sorted_numbers =sorted(lucky_numbers)print(f"\n中奖号码(共{len(sorted_numbers)}个):")print(sorted_numbers)# 格式化输出print("\n中奖名单:")for i, num inenumerate(sorted_numbers, 1):print(f"第{i:2d}位幸运儿:工号 {num:03d}")print("\n"+"="*50)print("恭喜以上老师!请凭工号领取奖品!")print("="*50)
四、代码解析
关键知识点串联:
1.random模块:我们在第4章学习过random库,这里使用randint(1, 800)生成指定范围的随机整数
2.while循环:第4章学习的无限循环,条件是len(lucky_numbers) < 30,当集合元素达到30个时自动停止
3.集合的add()方法:第5章本节刚学过,用于添加单个元素,重复添加自动忽略
4.len()函数:第2章学习的内置函数,用于获取集合元素个数
5.sorted()函数:将集合转换为排序后的列表,因为集合本身无序
6.enumerate()函数:第4章学习的遍历技巧,用于给中奖者编号
7.字符串格式化:第2章学习的f-string和格式化输出,{i:2d}表示占2位宽度,{num:03d}表示占3位宽度不足补0
五、程序优化与扩展
import randomdef draw_lottery(total_people, winners_count, seed=None):"""通用抽奖函数参数:total_people: 总人数winners_count: 中奖人数seed: 随机种子(可选,用于复现结果)返回:中奖号码列表(已排序)"""if winners_count > total_people:raiseValueError("中奖人数不能大于总人数!")if seed:random.seed(seed)# 使用集合去重winners =set()whilelen(winners) < winners_count:winners.add(random.randint(1, total_people))returnsorted(list(winners))# 测试:CK学校不同奖项print("="*60)print("🏆 CK学校2025年年会抽奖系统 🏆")print("="*60)# 特等奖1名grand_prize = draw_lottery(800, 1)print(f"\n🥇 特等奖(1名):{grand_prize}")# 一等奖3名first_prize = draw_lottery(800, 3)print(f"\n🥈 一等奖(3名):{first_prize}")# 二等奖10名second_prize = draw_lottery(800, 10)print(f"\n🥉 二等奖(10名):{second_prize}")# 三等奖30名(使用随机种子确保可复现)third_prize = draw_lottery(800, 30, seed=2025)print(f"\n🏅 三等奖(30名):{third_prize[:10]}... 等共30人")# 验证三等奖确实有30个不重复号码print(f"\n验证:三等奖实际人数 = {len(set(third_prize))}(应为30)")

【AI辅助学习环节】
🤖 与AI一起探索集合的趣味应用
本节将设计一系列有趣的Python编程练习,帮助你通过与AI对话来巩固集合知识,同时学习一些有趣的编程技巧。

练习1:用集合分析重庆美食偏好
场景:CK学校对数字经济专业学生进行美食调查,收集大家最喜欢的重庆美食。
你的任务: 1. 向AI提问:“如何用Python集合统计两组学生的重庆美食偏好差异?” 2. 尝试理解AI给出的代码 3. 修改代码,加入你自己喜欢的美食
参考提示词:
请用Python编写一个程序,使用集合来比较两个班级的重庆美食偏好。班级A喜欢:火锅、小面、酸辣粉、毛血旺班级B喜欢:火锅、串串香、辣子鸡、酸辣粉要求:1. 找出两个班级都喜欢的美食(交集)2. 找出只有班级A喜欢的美食(差集)3. 找出两个班级所有喜欢的美食(并集)4. 用中文输出结果,格式美观

练习2:用turtle绘制动态爱心
场景:用编程表达你对编程学习的热爱!
你的任务: 1. 向AI提问:“请用Python turtle库绘制一个跳动的红色爱心,并添加文字’I Love Python’” 2. 运行AI给出的代码 3. 尝试修改颜色、大小、文字内容
参考提示词:
请用Python的turtle库绘制一个动态跳动的红色爱心图案。要求:1. 爱心要填充红色,边框深红色2. 添加动画效果,让爱心有"跳动"的感觉(可以通过改变大小实现)3. 在爱心下方添加文字"CK学校 - Python学习之旅"4. 添加一些装饰,比如小星星或彩色粒子5. 代码要有详细注释,方便初学者理解

练习3:用集合生成词云数据
场景:分析CK学校数字经济专业学生对Python学习的关键词。
你的任务: 1. 向AI提问:“如何用Python集合和jieba分词分析一段关于Python学习的文本,提取关键词?” 2. 提供一段你自己的学习心得给AI 3. 让AI帮你生成词云数据
参考提示词:
请用Python编写一个程序,使用jieba分词和集合来提取文本关键词。文本内容:"Python是一种简单易学但功能强大的编程语言。学习Python让我感受到了编程的乐趣。Python在数据分析、人工智能、Web开发等领域都有广泛应用。作为CK学校数字经济专业的学生,掌握Python对未来的职业发展非常重要。"要求:1. 使用jieba分词提取词语2. 使用集合去重,只保留有意义的词汇(去掉"是"、"的"等停用词)3. 统计每个词出现的频率4. 输出出现频率最高的10个词

练习4:集合与数学——维恩图可视化
场景:用图形化方式理解集合运算。
你的任务: 1. 向AI提问:“请用Python matplotlib绘制两个集合的交集、并集、差集的维恩图(Venn Diagram)” 2. 理解代码中集合运算的部分 3. 修改数据为你自己的学习场景(如”已掌握的知识点”和”需要学习的知识点”)
参考提示词:
请用Python的matplotlib库绘制维恩图(Venn Diagram),展示以下集合关系:集合A(已学知识):变量、数据类型、分支结构、循环结构、列表、元组集合B(考试重点):变量、数据类型、分支结构、循环结构、集合、字典要求:1. 绘制两个圆的维恩图2. 标注交集(共同重点)、A独有、B独有的部分3. 添加标题和图例4. 使用不同颜色区分不同区域5. 代码要包含详细的注释说明

练习5:集合去重实战——清理重复数据
场景:CK学校教务处有一份有重复记录的学生成绩表,需要清理。
你的任务: 1. 向AI提问:“如何用Python集合快速去除CSV文件中的重复学生记录?” 2. 让AI生成完整的代码示例 3. 理解代码中集合的应用
参考提示词:
请用Python编写一个数据清洗程序,使用集合去除重复数据。场景:有一个学生成绩列表,包含重复记录数据示例:[{"学号": "2023001", "姓名": "张三", "成绩": 85},{"学号": "2023002", "姓名": "李四", "成绩": 90},{"学号": "2023001", "姓名": "张三", "成绩": 85},# 重复{"学号": "2023003", "姓名": "王五", "成绩": 78}]要求:1. 使用集合识别重复记录(基于学号)2. 保留唯一记录3. 输出清洗前后的数据量对比4. 显示被删除的重复记录

💡 与AI对话的技巧提示
1.明确需求:告诉AI你想要什么功能,使用什么技术(如”使用Python集合”)
2.提供背景:说明你的应用场景(如”CK学校的学生管理”)
3.指定输出:要求代码有注释、格式美观、易于理解
4.迭代优化:如果AI的回答不满意,可以继续提问”请简化代码”或”请添加错误处理”
5.动手实践:复制AI的代码到Jupyter Notebook中运行,观察结果

📝 课后自主练习
请完成以下练习,并使用AI辅助检查你的答案:
练习A:创建一个包含你本学期所有课程名称的集合,然后: 1. 添加一门新课程 2. 删除一门已结课的课程 3. 检查”Python基础及应用”是否在集合中
练习B:假设你有两个集合: - morning_courses:上午的课程 - afternoon_courses:下午的课程 请用集合运算找出: 1. 全天的课程安排(并集) 2. 可能的时间冲突(交集) 3. 只在上午或只在下午的课程(对称差集)
练习C(挑战):用集合实现一个简单的拼写检查器: - 创建一个包含正确单词的集合(词典) - 输入一个单词,检查是否在词典中 - 如果不在,给出提示

【本节小结】
核心知识点回顾
1.集合的特点:无序、不重复、元素不可变
2.创建方式:{元素1, 元素2} 或 set(可迭代对象)
3.集合运算:并集|、交集&、差集-、对称差集^
4.常用方法:add()、update()、remove()、discard()、pop()、clear()
5.应用场景:去重、成员检测、数学集合运算
