集合(Set)

学习Python集合的创建、操作和特性,掌握这个用于存储唯一元素的无序容器。

基本概念

集合(Set)是Python中的一种数据结构,用于存储不重复的元素。集合中的元素是无序的,且每个元素必须是不可变类型。

集合的特点:

  • 无序:集合中的元素没有特定的顺序
  • 唯一:集合中的元素不能重复
  • 可变:可以添加和删除元素
  • 元素必须是不可变类型:可以是数字、字符串、元组等,不能是列表、字典、集合等可变类型
  • 没有索引:不能通过索引访问元素

集合的表示:用花括号 {} 包围元素,多个元素之间用逗号 , 分隔。注意:创建空集合不能使用 {},因为这会创建一个空字典,应该使用 set() 函数。

# 创建一个空集合
empty_set = set()
# 注意:不能使用 {} 创建空集合,这会创建一个空字典
empty_dict = {}

# 创建包含元素的集合
fruits = {'apple', 'banana', 'orange', 'apple'}
print(fruits) # 输出: {'banana', 'apple', 'orange'}(注意:重复的'apple'被自动移除了)

# 使用set()函数从其他可迭代对象创建集合
numbers_list = [1, 2, 3, 2, 4, 5, 3]
numbers_set = set(numbers_list)
print(numbers_set) # 输出: {1, 2, 3, 4, 5}(重复的元素被自动移除)

# 从字符串创建集合
word = 'hello'
char_set = set(word)
print(char_set) # 输出类似: {'h', 'e', 'l', 'o'}(重复的'l'被自动移除)

# 从元组创建集合
colors_tuple = ('red', 'green', 'blue', 'red')
colors_set = set(colors_tuple)
print(colors_set) # 输出: {'red', 'green', 'blue'}(重复的'red'被自动移除)

集合的基本操作

我们可以对集合进行添加、删除元素等基本操作。

# 创建一个集合
fruits = {'apple', 'banana', 'orange'}

# 添加单个元素
fruits.add('grape')
print(fruits) # 输出类似: {'banana', 'apple', 'orange', 'grape'}

# 尝试添加重复元素(不会有效果)
fruits.add('apple')
print(fruits) # 输出和上面相同,因为'apple'已经在集合中

# 添加多个元素
fruits.update(['watermelon', 'pear', 'pineapple'])
print(fruits) # 输出类似: {'banana', 'apple', 'orange', 'grape', 'watermelon', 'pear', 'pineapple'}

# 删除指定元素(如果元素不存在会引发KeyError)
fruits.remove('orange')
print(fruits) # 输出类似: {'banana', 'apple', 'grape', 'watermelon', 'pear', 'pineapple'}

# 删除指定元素(如果元素不存在不会引发错误)
fruits.discard('orange') # 不会引发错误,因为'orange'已经不在集合中
print(fruits) # 输出和上面相同

# 随机删除并返回一个元素
random_fruit = fruits.pop()
print("随机删除的元素: ", random_fruit)
print(fruits) # 输出中不再包含被删除的元素

# 清空集合
fruits.clear()
print(fruits) # 输出: set()

# 删除整个集合
del fruits
# print(fruits) # 引发NameError,因为fruits已经不存在

我们还可以使用 in 关键字来检查某个元素是否在集合中:

# 创建一个集合
numbers = {1, 2, 3, 4, 5}

# 检查元素是否在集合中
if 3 in numbers:
    print("3在集合中")
else:
    print("3不在集合中")

if 6 not in numbers:
    print("6不在集合中")

集合运算

集合支持多种数学集合运算,如并集、交集、差集和对称差集等。

并集(Union)

两个集合中所有的元素(去除重复)

符号:| 或方法:union()

交集(Intersection)

两个集合中共同的元素

符号:& 或方法:intersection()

差集(Difference)

在第一个集合中但不在第二个集合中的元素

符号:- 或方法:difference()

对称差集(Symmetric Difference)

在两个集合中但不在它们交集中的元素

符号:^ 或方法:symmetric_difference()

# 创建两个集合
set_a = {1, 2, 3, 4, 5}
set_b = {3, 4, 5, 6, 7}

# 并集
union_set = set_a | set_b
print("并集: ", union_set) # 输出: {1, 2, 3, 4, 5, 6, 7}
# 或者使用union()方法
union_set = set_a.union(set_b)
print("并集: ", union_set) # 输出: {1, 2, 3, 4, 5, 6, 7}

# 交集
intersection_set = set_a & set_b
print("交集: ", intersection_set) # 输出: {3, 4, 5}
# 或者使用intersection()方法
intersection_set = set_a.intersection(set_b)
print("交集: ", intersection_set) # 输出: {3, 4, 5}

# 差集
difference_set = set_a - set_b
print("差集 (set_a - set_b): ", difference_set) # 输出: {1, 2}
# 或者使用difference()方法
difference_set = set_a.difference(set_b)
print("差集 (set_a - set_b): ", difference_set) # 输出: {1, 2}

# 反过来的差集
difference_set = set_b - set_a
print("差集 (set_b - set_a): ", difference_set) # 输出: {6, 7}

# 对称差集
sym_diff_set = set_a ^ set_b
print("对称差集: ", sym_diff_set) # 输出: {1, 2, 6, 7}
# 或者使用symmetric_difference()方法
sym_diff_set = set_a.symmetric_difference(set_b)
print("对称差集: ", sym_diff_set) # 输出: {1, 2, 6, 7}

集合的包含关系

我们可以检查一个集合是否是另一个集合的子集、超集或不相交集。

子集(Subset)

如果集合A的所有元素都在集合B中,则A是B的子集

符号:<= 或方法:issubset()

真子集(Proper Subset)

如果集合A是B的子集且A不等于B,则A是B的真子集

符号:<

超集(Superset)

如果集合B的所有元素都在集合A中,则A是B的超集

符号:>= 或方法:issuperset()

真超集(Proper Superset)

如果集合A是B的超集且A不等于B,则A是B的真超集

符号:>

不相交集(Disjoint)

如果两个集合没有共同的元素,则它们是不相交的

方法:isdisjoint()

# 创建三个集合
a = {1, 2, 3}
b = {1, 2, 3, 4, 5}
c = {6, 7, 8}

# 检查子集关系
print("a是b的子集吗? ", a <= b) # 输出: True
print("a是b的真子集吗? ", a < b) # 输出: True
print("b是a的子集吗? ", b <= a) # 输出: False
# 或者使用issubset()方法
print("a是b的子集吗? ", a.issubset(b)) # 输出: True

# 检查超集关系
print("b是a的超集吗? ", b >= a) # 输出: True
print("b是a的真超集吗? ", b > a) # 输出: True
print("a是b的超集吗? ", a >= b) # 输出: False
# 或者使用issuperset()方法
print("b是a的超集吗? ", b.issuperset(a)) # 输出: True

# 检查不相交集
print("a和c不相交吗? ", a.isdisjoint(c)) # 输出: True
print("a和b不相交吗? ", a.isdisjoint(b)) # 输出: False

故事化案例:学生选课系统

在学校的选课系统中,我们需要管理学生选择的课程。由于每个学生不能选择同一门课程多次,集合是解决这个问题的理想选择。

场景:学生选课系统

  • 为每个学生创建一个课程集合
  • 添加和删除选课
  • 检查学生是否选了某门课程
  • 找出两个学生共同选择的课程
  • 找出一个学生选了但另一个学生没选的课程
  • 统计每个学生选课的数量

代码实现:

# 创建三个学生的选课集合
student1_courses = {'数学', '语文', '英语', '物理', '化学'}
student2_courses = {'数学', '英语', '生物', '历史', '地理'}
student3_courses = {'语文', '物理', '化学', '政治', '音乐'}

# 检查学生是否选了某门课程
def check_course(student_name, courses, course_name:
    if course_name in courses:
        print(f"{student_name}选了{course_name}")
    else:
        print(f"{student_name}没有选{course_name}")

check_course("学生1", student1_courses, "数学")
check_course("学生1", student1_courses, "历史")
check_course("学生2", student2_courses, "生物")

# 学生添加新选课
student1_courses.add("计算机")
print("\n学生1添加计算机课后的选课: ", student1_courses)

# 学生退课
student2_courses.discard("地理")
print("\n学生2退掉地理课后的选课: ", student2_courses)

# 找出两个学生共同选择的课程(交集)
common_courses = student1_courses & student2_courses
print("\n学生1和学生2共同选择的课程: ", common_courses)

# 找出学生1选了但学生2没选的课程(差集)
student1_only = student1_courses - student2_courses
print("\n学生1选了但学生2没选的课程: ", student1_only)

# 找出两个学生一共选了哪些课程(并集)
all_courses = student1_courses.union(student2_courses)
print("\n学生1和学生2一共选了这些课程: ", all_courses)

# 统计每个学生选课的数量
print("\n学生1选课数量: ", len(student1_courses))
print("学生2选课数量: ", len(student2_courses))
print("学生3选课数量: ", len(student3_courses))

# 找出三个学生都选了的课程
common_all = student1_courses.intersection(student2_courses, student3_courses)
print("\n三个学生都选了的课程: ", common_all) # 输出可能为空集合

# 找出至少有一个学生选了的课程
all_courses_all = student1_courses.union(student2_courses, student3_courses)
print("\n至少有一个学生选了的课程总数: ", len(all_courses_all))
print(all_courses_all)

运行上面的代码,输出结果为:
学生1选了数学
学生1没有选历史
学生2选了生物

学生1添加计算机课后的选课: {'数学', '语文', '英语', '物理', '化学', '计算机'}

学生2退掉地理课后的选课: {'数学', '英语', '生物', '历史'}

学生1和学生2共同选择的课程: {'数学', '英语'}

学生1选了但学生2没选的课程: {'语文', '物理', '化学', '计算机'}

学生1和学生2一共选了这些课程: {'数学', '语文', '英语', '物理', '化学', '生物', '历史', '计算机'}

学生1选课数量: 6
学生2选课数量: 4
学生3选课数量: 5

三个学生都选了的课程: set()

至少有一个学生选了的课程总数: 11
{'数学', '语文', '英语', '物理', '化学', '生物', '历史', '政治', '音乐', '计算机'}

练习题

1 集合基本操作

1. 创建一个包含你喜欢的5种颜色的集合 2. 添加一种新的颜色 3. 尝试添加一种已经存在的颜色(观察会发生什么) 4. 删除一种颜色 5. 尝试删除一种不存在的颜色(使用discard方法) 6. 检查集合中是否包含特定的颜色 7. 统计集合中元素的数量

2 找出共同朋友

1. 创建两个集合,表示你和你的朋友各自的朋友圈 2. 找出你们共同的朋友(交集) 3. 找出只在你朋友圈中的朋友(差集) 4. 找出只在你朋友朋友圈中的朋友(差集) 5. 找出你们所有的朋友(并集) 6. 找出只在一方朋友圈中的朋友(对称差集)

3 去除列表中的重复元素

1. 创建一个包含重复元素的列表 2. 使用集合去除列表中的重复元素 3. 将去重后的集合转回列表 4. 保持原列表中元素的顺序 5. 编写一个函数,接受一个列表作为参数,返回一个去除重复元素并保持原有顺序的新列表

4 集合关系判断

1. 创建三个集合:A(包含1, 2, 3, 4, 5)、B(包含3, 4, 5)、C(包含6, 7, 8) 2. 判断B是否是A的子集 3. 判断A是否是B的超集 4. 判断A和C是否没有交集(即是否不相交) 5. 验证A和B的交集是否等于B

5 模拟用户兴趣标签管理

1. 创建一个表示用户兴趣标签的集合 2. 添加新的兴趣标签 3. 移除不再感兴趣的标签 4. 检查用户是否对某个标签感兴趣 5. 与另一个用户的兴趣标签进行比较,找出共同兴趣 6. 找出只在一方感兴趣的标签 7. 计算用户兴趣标签的总数