# 第 1-11 课:集合 ## 一、本课目标 完成本课后,你将能够: 1. 理解集合是什么,以及集合适合解决什么问题; 2. 使用 `{}` 或 `set()` 创建集合; 3. 理解集合中元素唯一、集合本身无序; 4. 使用 `add()`、`update()` 添加元素; 5. 使用 `remove()`、`discard()`、`pop()` 和 `clear()` 删除元素; 6. 使用 `in` 和 `not in` 判断元素是否存在; 7. 遍历集合; 8. 使用交集、并集、差集和对称差集; 9. 使用集合为列表去重; 10. 根据实际需求在列表、元组、字典和集合之间做出选择。 ## 二、前置知识 学习本课前,应当已经了解: - 变量与常见数据类型; - 条件判断; - `for` 循环; - 字符串; - 列表和元组; - 字典; - `in` 和 `not in`。 ## 三、集合是什么 集合(Set)是一种保存多个元素的数据结构。 它有两个非常重要的特点: 1. **元素不能重复**; 2. **元素没有固定位置**。 例如,一个 Agent 可以拥有多个工具权限: ```python agent_tools = {"搜索", "计算", "写作"} ``` 如果重复写入同一个工具,集合只会保留一份: ```python agent_tools = {"搜索", "计算", "搜索"} print(agent_tools) ``` 集合中最终只有两个元素。 集合特别适合处理: - 去除重复数据; - 快速判断某个元素是否存在; - 比较两组数据有哪些相同项或不同项; - 权限、标签、课程、技能等不允许重复的数据。 ## 四、创建集合 ### 4.1 使用花括号创建 ```python agent_tools = {"搜索", "计算", "写作"} print(agent_tools) ``` 集合使用花括号,但它与字典不同: ```python # 集合:里面直接保存元素 agent_tools = {"搜索", "计算"} # 字典:里面保存“键: 值” agent_config = {"model": "gpt-5", "enabled": True} ``` ### 4.2 使用 `set()` 创建 ```python agent_tools = set(["搜索", "计算", "搜索"]) print(agent_tools) ``` `set()` 会把传入的数据转换成集合,并自动去除重复元素。 ### 4.3 创建空集合 空集合必须写成: ```python empty_tools = set() ``` 不能写成: ```python empty_tools = {} ``` 因为 `{}` 创建的是空字典,不是空集合。 可以使用 `type()` 验证: ```python print(type(set())) print(type({})) ``` ## 五、集合为什么不支持下标 列表和元组中的元素有明确位置,所以可以写: ```python models = ["gpt-5", "o3"] print(models[0]) ``` 集合是无序数据结构,不保证每个元素一直处于某个固定位置,因此不能写: ```python agent_tools = {"搜索", "计算"} print(agent_tools[0]) # 错误 ``` 这里的“无序”不是说 Python 每次一定用不同顺序显示,而是说: > 不应该依赖集合元素的显示顺序,也不能使用下标读取集合元素。 如果业务需要稳定顺序,应使用列表或元组。 ## 六、集合元素必须唯一 ```python models = {"gpt-5", "o3", "gpt-5"} print(len(models)) ``` 结果为: ```text 2 ``` 第二个 `"gpt-5"` 不会产生新元素,也不会报错。 ## 七、判断元素是否存在 集合经常配合 `in` 和 `not in` 使用: ```python agent_tools = {"搜索", "计算"} print("搜索" in agent_tools) print("写作" not in agent_tools) ``` 正常结果: ```text True True ``` 集合非常擅长成员判断。数据较多时,通常比逐个检查列表更合适。 ## 八、添加元素 ### 8.1 使用 `add()` 添加一个元素 ```python agent_tools = {"搜索", "计算"} agent_tools.add("写作") print(agent_tools) ``` 如果添加已经存在的元素,集合不会发生变化: ```python agent_tools.add("搜索") ``` ### 8.2 使用 `update()` 添加多个元素 ```python agent_tools = {"搜索"} agent_tools.update(["计算", "写作"]) print(agent_tools) ``` 注意: - `add()` 添加一个完整元素; - `update()` 从列表、元组、集合等可遍历数据中逐个取出元素并添加。 ## 九、删除元素 ### 9.1 `remove()`:元素不存在时会报错 ```python agent_tools = {"搜索", "计算"} agent_tools.remove("搜索") ``` 如果继续删除不存在的 `"搜索"`,会出现 `KeyError`,中文意思是找不到该元素。 ### 9.2 `discard()`:元素不存在也不会报错 ```python agent_tools = {"搜索", "计算"} agent_tools.discard("写作") ``` 当你不确定元素是否存在时,`discard()` 通常更安全。 ### 9.3 `pop()`:删除并返回一个不确定的元素 ```python agent_tools = {"搜索", "计算"} removed_tool = agent_tools.pop() print(removed_tool) ``` 集合没有固定顺序,所以不能依赖 `pop()` 删除某个指定元素。 如果集合为空,调用 `pop()` 会报错。 ### 9.4 `clear()`:清空集合 ```python agent_tools = {"搜索", "计算"} agent_tools.clear() print(agent_tools) ``` 结果是空集合: ```text set() ``` ## 十、遍历集合 集合可以使用 `for` 循环遍历: ```python agent_tools = {"搜索", "计算", "写作"} for tool in agent_tools: print(tool) ``` 变量名 `tool` 可以换成其他合法名称,但应当选择能够表达含义的名字。 不要依赖遍历顺序。如果必须按顺序输出,可以先使用 `sorted()` 排序: ```python for tool in sorted(agent_tools): print(tool) ``` `sorted()` 会返回一个排好序的新列表,不会修改原集合。 ## 十一、交集:两组数据共同拥有的元素 交集可以使用 `&` 或 `intersection()`: ```python code_agent_tools = {"搜索", "计算", "代码执行"} writer_agent_tools = {"搜索", "写作", "图片生成"} common_tools = code_agent_tools & writer_agent_tools print(common_tools) ``` 结果: ```text {'搜索'} ``` 也可以写成: ```python common_tools = code_agent_tools.intersection(writer_agent_tools) ``` ## 十二、并集:合并两组数据并自动去重 并集可以使用 `|` 或 `union()`: ```python all_tools = code_agent_tools | writer_agent_tools print(all_tools) ``` 结果包含两组集合的全部工具,重复的 `"搜索"` 只保留一份。 ## 十三、差集:一组有、另一组没有的元素 差集使用 `-` 或 `difference()`: ```python code_only_tools = code_agent_tools - writer_agent_tools print(code_only_tools) ``` 它表示代码助手拥有、写作助手没有的工具。 注意方向: ```python code_agent_tools - writer_agent_tools ``` 和: ```python writer_agent_tools - code_agent_tools ``` 结果通常不同。 ## 十四、对称差集:只属于其中一组的元素 对称差集使用 `^` 或 `symmetric_difference()`: ```python different_tools = code_agent_tools ^ writer_agent_tools print(different_tools) ``` 它会排除两组共同拥有的元素,只保留各自独有的元素。 ## 十五、集合之间的关系判断 如果集合 A 中的所有元素都在集合 B 中,A 就是 B 的子集: ```python required_tools = {"搜索", "计算"} available_tools = {"搜索", "计算", "写作"} print(required_tools <= available_tools) ``` 结果为 `True`。 也可以使用: ```python print(required_tools.issubset(available_tools)) print(available_tools.issuperset(required_tools)) ``` - 子集(Subset):较小集合中的元素全部包含在较大集合中; - 超集(Superset):较大集合包含较小集合的全部元素。 这很适合判断“当前权限是否满足所需权限”。 ## 十六、使用集合为列表去重 ```python model_history = ["gpt-5", "o3", "gpt-5", "o3", "gpt-5-mini"] unique_models = set(model_history) print(unique_models) ``` 如果最终仍然需要列表,可以再转换: ```python unique_model_list = list(unique_models) ``` 注意:这种方法不保证保留原列表顺序。 当前阶段如果既要去重又要保留顺序,可以使用: ```python unique_model_list = [] for model in model_history: if model not in unique_model_list: unique_model_list.append(model) ``` ## 十七、哪些数据可以放入集合 集合元素必须是不可变且可哈希(Hashable)的数据。 当前阶段可以简单理解为: - 字符串、数字、布尔值、元组通常可以放入集合; - 列表、字典、普通集合不能直接放入集合。 下面会报错: ```python invalid_set = {[1, 2], [3, 4]} ``` 因为列表可以被修改,不能作为集合元素。 “哈希”的内部原理将在进阶阶段逐步补充,本课只需要记住常见规则。 ## 十八、列表、元组、字典和集合如何选择 ### 使用列表 - 需要保留顺序; - 允许重复; - 需要使用下标读取或修改。 ### 使用元组 - 需要保留顺序; - 允许重复; - 创建后不希望修改整体结构。 ### 使用字典 - 需要使用“键”描述每个“值”; - 需要通过字段名称快速读取数据。 ### 使用集合 - 不关心顺序; - 不允许重复; - 经常进行成员判断、去重或集合运算。 ## 十九、运行本课示例 进入项目根目录: ```powershell cd D:\Code\Python ``` 运行示例: ```powershell python .\01_python基础\1_11_集合\sets.py ``` 运行练习: ```powershell python .\01_python基础\1_11_集合\practice.py ``` 集合的显示顺序可能与讲义不同,只要元素相同就是正常现象。 ## 二十、常见错误 ### 20.1 使用 `{}` 创建空集合 `{}` 是空字典,空集合必须使用 `set()`。 ### 20.2 使用下标读取集合 集合不支持下标。如果需要按位置读取,应使用列表或元组。 ### 20.3 使用 `remove()` 删除不存在的元素 不确定元素是否存在时,可以先用 `in` 判断,或者使用 `discard()`。 ### 20.4 依赖集合的输出顺序 集合不保证业务上的稳定顺序。需要排序展示时使用 `sorted()`。 ### 20.5 把列表或字典放入集合 列表和字典是可变数据,不能作为集合元素。 ### 20.6 混淆 `add()` 和 `update()` `add()` 添加一个元素;`update()` 从其他可遍历数据中添加多个元素。 ## 二十一、课堂练习 打开: ```text 01_python基础/1_11_集合/practice.py ``` 本课练习将创建一个“Agent 工具权限管理器”,内容包括: 1. 创建集合; 2. 检查、添加和删除权限; 3. 遍历权限; 4. 比较两组权限的交集、并集、差集和对称差集; 5. 判断所需权限是否是可用权限的子集; 6. 为模型使用记录去重。 请先独立完成。遇到问题时,我会先给出定位提示,不会直接覆盖你的答案。 ## 二十二、思考题 1. 为什么 `{}` 不能表示空集合? 2. 为什么不能使用 `agent_tools[0]` 读取集合? 3. `remove()` 和 `discard()` 有什么区别? 4. `A - B` 与 `B - A` 为什么可能不同? 5. 使用集合为列表去重时,可能丢失什么信息? ## 二十三、本课小结 本课最重要的知识点: 1. 集合使用 `set` 类型表示; 2. 集合元素唯一,且没有可依赖的固定顺序; 3. 空集合必须使用 `set()` 创建; 4. `add()` 添加一个元素,`update()` 添加多个元素; 5. `remove()` 删除不存在的元素会报错,`discard()` 不会; 6. 集合支持交集、并集、差集和对称差集; 7. 集合适合去重、成员判断和权限比较。 ## 二十四、验收标准 完成练习后,应满足: - 能正确创建非空集合和空集合; - 能解释集合为什么不支持下标; - 能使用 `in` 判断元素; - 能正确添加、删除和遍历集合元素; - 能计算两组集合的交集、并集、差集和对称差集; - 能判断子集关系; - 能使用集合完成列表去重; - 示例和练习均可正常运行。