第五章:集合
5.1 什么是集合?
集合就像 一个没有重复元素的魔法袋子,里面的元素没有顺序,每个元素都是唯一的,而且集合会自动去除重复的元素。
# 创建集合
fruits = {"苹果", "香蕉", "橙子", "苹果"} # 重复的"苹果"会被自动去除
numbers = {1, 2, 3, 4, 5, 3, 2, 1} # 重复的数字会被去除
5.2 集合的创建方式
集合可以用多种方式创建:
# 直接使用花括号
fruits = {"苹果", "香蕉", "橙子"}
# 使用set()函数从其他序列创建
numbers = set([1, 2, 3, 4, 5]) # 从列表创建
chars = set("hello") # 从字符串创建,得到:{'h', 'e', 'l', 'o'}
mixed = set([1, "hello", 3.14, True]) # 可以包含不同类型的元素
# 创建空集合
empty_set = set()
# 注意:empty_set = {} 创建的是空字典,不是空集合
# 使用集合推导式
squares = {x**2 for x in range(1, 6)} # {1, 4, 9, 16, 25}
5.3 集合的基本特性
5.3.1 无序性
集合中的元素没有固定的顺序:
fruits = {"苹果", "香蕉", "橙子"}
print(fruits) # 每次输出的顺序可能不同
5.3.2 唯一性
集合会自动去除重复元素:
numbers = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4}
print(numbers) # {1, 2, 3, 4} - 重复元素被自动去除
5.3.3 不可索引
由于集合是无序的,不能通过索引访问元素:
fruits = {"苹果", "香蕉", "橙子"}
# print(fruits[0]) # 这会报错!集合不支持索引
5.4 集合的基本操作
5.4.1 集合长度
使用 len() 函数获取集合中元素的数量:
fruits = {"苹果", "香蕉", "橙子"}
print(len(fruits)) # 3
5.4.2 检查元素是否存在
使用 in 关键字检查元素是否在集合中:
fruits = {"苹果", "香蕉", "橙子"}
print("苹果" in fruits) # True
print("葡萄" in fruits) # False
5.4.3 集合的遍历
虽然集合是无序的,但可以遍历所有元素:
fruits = {"苹果", "香蕉", "橙子"}
# 直接遍历元素
for fruit in fruits:
print(f"水果:{fruit}")
# 使用enumerate获取序号(但序号不代表固定位置)
for i, fruit in enumerate(fruits):
print(f"第{i}个水果是:{fruit}")
5.5 集合的常用方法
5.5.1 添加元素
fruits = {"苹果", "香蕉"}
# add() - 添加单个元素
fruits.add("橙子")
print(fruits) # {'苹果', '香蕉', '橙子'}
# update() - 添加多个元素
fruits.update(["葡萄", "芒果"])
print(fruits) # 添加了葡萄和芒果
# 添加已存在的元素不会有任何效果
fruits.add("苹果")
print(fruits) # 集合不变
5.5.2 删除元素
fruits = {"苹果", "香蕉", "橙子", "葡萄"}
# remove() - 删除指定元素,如果元素不存在则报错
fruits.remove("香蕉")
print(fruits) # 删除了香蕉
# discard() - 删除指定元素,如果元素不存在也不报错
fruits.discard("西瓜") # 西瓜不存在,但不报错
# pop() - 随机删除并返回一个元素(因为集合无序)
removed_fruit = fruits.pop()
print(f"删除了:{removed_fruit}")
print(fruits)
# clear() - 清空集合
fruits.clear()
print(fruits) # set()
5.6 集合的数学运算
集合支持多种数学运算,这是集合最强大的特性之一。
5.6.1 并集
包含两个集合中所有的元素:
A = {1, 2, 3}
B = {3, 4, 5}
# 使用 | 运算符
print(A | B) # {1, 2, 3, 4, 5}
# 使用 union() 方法
print(A.union(B)) # {1, 2, 3, 4, 5}
5.6.2 交集
包含两个集合中共有的元素:
A = {1, 2, 3}
B = {3, 4, 5}
# 使用 & 运算符
print(A & B) # {3}
# 使用 intersection() 方法
print(A.intersection(B)) # {3}
5.6.3 差集
包含在第一个集合中但不在第二个集合中的元素:
A = {1, 2, 3}
B = {3, 4, 5}
# 使用 - 运算符
print(A - B) # {1, 2}
# 使用 difference() 方法
print(A.difference(B)) # {1, 2}
5.6.4 对称差集
包含两个集合中不重复的元素(即只属于其中一个集合,不属于交集的元素):
A = {1, 2, 3}
B = {3, 4, 5}
# 使用 ^ 运算符
print(A ^ B) # {1, 2, 4, 5}
# 使用 symmetric_difference() 方法
print(A.symmetric_difference(B)) # {1, 2, 4, 5}
5.7 集合的关系运算
5.7.1 子集和超集
A = {1, 2, 3}
B = {1, 2}
# 子集检查
print(B <= A) # True - B是A的子集
print(B.issubset(A)) # True
# 超集检查
print(A >= B) # True - A是B的超集
print(A.issuperset(B)) # True
# 真子集/真超集
print(B < A) # True - B是A的真子集
print(A > B) # True - A是B的真超集
5.7.2 不相交集合
检查两个集合是否没有共同元素:
A = {1, 2, 3}
B = {4, 5, 6}
C = {3, 4, 5}
print(A.isdisjoint(B)) # True - A和B没有共同元素
print(A.isdisjoint(C)) # False - A和C有共同元素3
5.8 集合的更新操作
除了返回新集合的操作外,还有直接修改原集合的操作:
A = {1, 2, 3}
B = {3, 4, 5}
# update() - 用并集更新原集合
A.update(B)
print(A) # {1, 2, 3, 4, 5}
A = {1, 2, 3}
B = {3, 4, 5}
# intersection_update() - 用交集更新原集合
A.intersection_update(B)
print(A) # {3}
A = {1, 2, 3}
B = {3, 4, 5}
# difference_update() - 用差集更新原集合
A.difference_update(B)
print(A) # {1, 2}
A = {1, 2, 3}
B = {3, 4, 5}
# symmetric_difference_update() - 用对称差集更新原集合
A.symmetric_difference_update(B)
print(A) # {1, 2, 4, 5}
5.9 冻结集合
冻结集合是不可变的集合,因此可以作为字典的键或另一个集合的元素:
# 创建冻结集合
frozen = frozenset([1, 2, 3, 4, 5])
print(frozen) # frozenset({1, 2, 3, 4, 5})
# 冻结集合不可修改
# frozen.add(6) # 会报错!
# 冻结集合可以作为字典的键
dict_with_frozenset = {frozen: "这是一个冻结集合"}
print(dict_with_frozenset) # {frozenset({1, 2, 3, 4, 5}): '这是一个冻结集合'}
# 冻结集合也可以作为普通集合的元素
set_with_frozenset = {frozen, frozenset([6, 7, 8])}
print(set_with_frozenset)
5.10 集合的应用场景
5.10.1 数据去重
集合最常见的用途是去除序列中的重复元素:
# 列表去重
numbers = [1, 2, 2, 3, 3, 4, 5, 5]
unique_numbers = list(set(numbers))
print(unique_numbers) # [1, 2, 3, 4, 5](顺序可能不同)
# 字符串去重
text = "hello world"
unique_chars = ''.join(set(text))
print(unique_chars) # 类似 'helo wrd'(顺序可能不同)
5.10.2 成员测试
由于集合的成员测试效率很高(基于哈希表),适合用于快速检查元素是否存在:
# 大量数据成员测试时,集合比列表快很多
valid_users = {"user1", "user2", "user3", "user4", "user5"}
user_input = "user3"
if user_input in valid_users:
print("有效用户")
else:
print("无效用户")
5.10.3 数学运算
处理数学中的集合运算:
# 选课系统示例
math_students = {"Alice", "Bob", "Charlie"}
physics_students = {"Bob", "David", "Eve"}
# 同时选了两门课的学生
both = math_students & physics_students
print(both) # {'Bob'}
# 所有选课的学生
all_students = math_students | physics_students
print(all_students) # {'Alice', 'Bob', 'Charlie', 'David', 'Eve'}
# 只选了一门课的学生
only_one = math_students ^ physics_students
print(only_one) # {'Alice', 'Charlie', 'David', 'Eve'}
5.10.4 过滤数据
# 找出两个列表中的共同元素
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = set(list1) & set(list2)
print(common) # {4, 5}
# 找出只在一个列表中出现的元素
unique_to_list1 = set(list1) - set(list2)
print(unique_to_list1) # {1, 2, 3}
5.11 集合与列表、元组的比较
| 特性 | 列表 | 元组 | 集合 |
|---|---|---|---|
| 可变性 | 可变 | 不可变 | 可变(frozenset不可变) |
| 顺序 | 有序 | 有序 | 无序 |
| 重复元素 | 允许 | 允许 | 不允许 |
| 语法 | 方括号 [] | 圆括号 () | 花括号 {}(空集合用set()) |
| 索引 | 支持 | 支持 | 不支持 |
| 用途 | 存储有序序列 | 存储不应改变的有序序列 | 去重、成员测试、数学运算 |
5.12 集合的性能特点
集合基于哈希表实现,某些操作比列表高效:
import time
# 创建大量数据
large_list = list(range(1000000))
large_set = set(large_list)
# 测试成员检查性能
target = 999999
# 列表成员测试
start_time = time.time()
result = target in large_list
list_time = time.time() - start_time
# 集合成员测试
start_time = time.time()
result = target in large_set
set_time = time.time() - start_time
print(f"列表成员测试时间: {list_time:.6f} 秒")
print(f"集合成员测试时间: {set_time:.6f} 秒")
print(f"集合比列表快 {list_time/set_time:.1f} 倍")
本章笔记:
- 集合是一个无序且不重复的元素集。
- 集合使用花括号创建,但空集合必须使用set()。
- 集合会自动去除重复元素,元素没有固定顺序。
- 集合支持添加、删除等操作,但元素必须是可哈希的(不可变类型)。
- 集合支持丰富的数学运算:并集、交集、差集、对称差集等。
- 集合关系运算可以检查子集、超集和不相交。
- 冻结集合是不可变的集合。
- 集合常用于去重、快速成员测试和数学集合运算。
- 集合的成员测试效率远高于列表。