CC++ & Algorithm

Python元组 tuple 和集合 set

较难4
语言版本:C++Python
概述:元组是只读的列表,集合是没有重复元素的无序盒子。

Python元组和集合:不变的便签和自动去重的百宝袋

除了之前学过的列表(可变的有序盒子)和字典(键值对映射),Python 还提供了两种特别实用的数据类型:元组(tuple)集合(set)。它们各有各的脾气,学会之后能让你的代码更安全、更高效。下面我们就来分别认识它们。

1. 元组(tuple):写上去就不能改的便签纸

元组长得像列表,但一旦创建,里面的元素就不能修改、增加或删除——就像一张写满字后贴死的便签纸。它用圆括号 () 来表示。

创建元组

# 用圆括号包裹,元素用逗号隔开
triangle_sides = (3, 4, 5)          # 三角形的三条边长,固定不变
print(triangle_sides[0])             # 可以访问,输出 3
# triangle_sides[0] = 6             # 这行如果执行会报错!元组不允许修改

如果元组只有一个元素,记得在它后面加一个逗号,否则 Python 会当成普通的数字或字符串:

single_tuple = (99,)                 # 正确:一个元素的元组
not_tuple = (99)                     # 错误:这只是整数 99,不是元组
print(type(single_tuple))            # <class 'tuple'>
print(type(not_tuple))               # <class 'int'>

元组的特点:只读、有序、可重复

  • 只读:不能修改、添加、删除任何元素。
  • 有序:元素有固定的顺序,可以用下标 [索引] 来访问(从0开始)。
  • 可重复:允许有相同元素,比如 (1,2,1) 是合法的。

生活中的例子

  • 一年中的月份:12个月份的名字是固定的,绝不会变。
months = ("一月", "二月", "三月", "四月", "五月", "六月",
          "七月", "八月", "九月", "十月", "十一月", "十二月")
print(months[0])   # 一月
  • 固定坐标点:比如你家的经纬度 (116.4, 39.9),不会随意改动。
  • 计算中的常量:比如圆周率 (3.14, 3.1416) 可以存进元组备用。

为什么用元组而不是列表?

场景推荐用元组理由
数据不应该被修改三角形的三边长度避免意外改动导致错误
作为字典的键字典的键必须是不可变的元组可以,列表不行
函数返回多个值返回一个元组安全、简洁

新手容易犯的错误

  • 试图修改元组t = (1,2,3); t[1] = 100 → 报错 TypeError。记住:元组不支持赋值。
  • 忘记单元素元组的逗号t = (1) 是整数,不是元组。调试时记得检查类型。

2. 集合(set):自动去重的“百宝袋”

集合就像一个魔法袋子,你往里丢东西,它会自动去掉重复的,而且不讲究顺序。它用花括号 {} 表示,但注意:空集合必须用 set() 创建,因为 {} 是空字典。

创建集合

# 用花括号直接创建
guests = {"小明", "小红", "小刚", "小明"}   # 小明重复了,集合自动保留一个
print(guests)  # 输出可能是 {'小红', '小刚', '小明'},顺序随机

# 创建空集合必须用 set()
empty_set = set()                           # 空集合
# empty_set = {}                            # 这其实是空字典,不是集合!

集合的特点:无序、不重复、元素必须可哈希(不可变)

  • 无序:元素没有固定的顺序,每次打印的顺序可能不同。不能通过下标访问。
  • 不重复:重复元素只保留一个。
  • 元素类型:只能放不可变的数据(数字、字符串、元组),不能放列表或字典。

生活中的例子

  • 活动签到名单:一个人不能重复签到,用集合自动去重。
  • 班级里不同的兴趣小组:统计所有参加的同学,去掉重复。
  • 数学中的集合:交集(都参加的人)、并集(所有参加的人)非常直观。

常用操作:添加、删除、长度

students = {"小刚", "小丽"}                 # 初始名单
students.add("小明")                        # 添加一个人
students.add("小丽")                        # 小丽已在,不影响
print(students)                             # 输出:{'小刚', '小丽', '小明'}

students.remove("小刚")                     # 删除一个人,如果不存在会报错
students.discard("小红")                    # 更安全:不存在不会报错
print(len(students))                        # 输出 2(小丽、小明)

集合的数学运算:交集、并集、差集

假设一班和二班都参加了运动会:

class1 = {"小明", "小红", "小刚"}
class2 = {"小刚", "小丽", "小亮"}

# 交集:两个班都参加的人(同时属于两个集合)
both = class1 & class2
print("共同参加的人:", both)               # {'小刚'}

# 并集:所有参加的人(合并两个集合,去掉重复)
all_students = class1 | class2
print("全部参加的人:", all_students)       # {'小刚', '小亮', '小红', '小丽', '小明'}

# 差集:只在一班但不在二班的人
only_class1 = class1 - class2
print("仅一班参加的人:", only_class1)      # {'小明', '小红'}

新手容易犯的错误

  • 混淆集合和字典{} 默认是字典,只有用 {元素1, 元素2} 才是集合。空集合必须用 set()
  • 试图用下标访问s = {1,2,3}; print(s[0]) → 报错,因为集合无序,没有索引。
  • 把可变元素(如列表)放入集合s = {[1,2]} → 报错,因为列表不可哈希。

3. 完整示例:班级考试信息管理

下面我们把元组和集合结合起来,处理一个班级的小任务:记录两名学生的考试成绩(不可变),然后统计班级参加补考的学生名单(去重)。

# ---------- 元组部分:存储每个学生的成绩 ----------
score_xiaoming = (95, 88, 92)          # 小明三门课的分数(语文, 数学, 英语)
score_xiaohong = (87, 93, 90)          # 小红三门课的分数
print("小明的成绩:", score_xiaoming)   # 输出 (95, 88, 92)
# score_xiaoming[0] = 100             # 如果取消注释会报错,成绩不能随意改

# ---------- 集合部分:记录需要补考的学生名单 ----------
# 假设考试不及格(低于60分)需要补考
need_retake = set()                     # 空集合,用来存放要补考的同学

# 判断小明是否需要补考
if score_xiaoming[0] < 60 or score_xiaoming[1] < 60 or score_xiaoming[2] < 60:
    need_retake.add("小明")

# 判断小红是否需要补考
if score_xiaohong[0] < 60 or score_xiaohong[1] < 60 or score_xiaohong[2] < 60:
    need_retake.add("小红")

# 再加一个同学小刚,他数学没及格
need_retake.add("小刚")
need_retake.add("小明")                # 小明已经在了,不会重复添加

print("需要补考的同学:", need_retake)  # 输出类似 {'小明', '小刚'}(小红全及格,不在集合中)

# ---------- 集合运算:求与另一个小班的交集 ----------
class_a = {"小明", "小红", "小刚", "小丽"}
class_b = {"小刚", "小亮", "小明"}

# 两个班都挂科的同学(交集)
fail_both = need_retake & class_b
print("B班需要补考的同学:", fail_both) # 输出 {'小刚', '小明'}

# 所有需要补考的同学(合并两个班)
all_fail = need_retake | class_b
print("所有需要补考的同学:", all_fail) # 输出 {'小刚', '小亮', '小明'}

运行这段代码,会看到元组保护了成绩不被修改,集合自动去重并帮助我们进行交集、并集运算。这就是元组和集合在日常编程中的典型用途。


4. 相关知识点指引

  • 列表:元组的“可变兄弟”,可以自由增删改。当你需要频繁修改数据时用列表。
  • 字典:另一种用花括号的有序映射,但键必须不可变(元组可用作键)。
  • 字符串:本质上也是不可变的序列,像元组一样只能读不能改。
  • 进阶知识:元组拆包(交换变量、函数返回多个值)、集合的 frozenset(不可变集合)、列表与集合互相转换(去重技巧)。

学会了元组和集合,你的 Python 工具箱就更丰富了。遇到“不想被改”的数据,果断用元组;遇到“要去重”或“做数学集合运算”的需求,优先考虑集合。多练几次,你会越来越喜欢它们!

例题精讲

1单选题

下列哪个操作会引发 TypeError?

At = (1, 2, 3); t[1] = 4
Bt = (1, 2, 3); print(t[1])
Ct = (1, [2,3], 4); t[1][0] = 5
Dt = [1,2,3]; t[1] = 4
2单选题

关于集合的说法,正确的是?

A集合中的元素可以是列表
B集合中的元素必须是不可变类型
C集合是有序的
D集合可以包含重复元素
3判断题

元组 (1, 2, 3) 可以通过 t[0] = 10 修改元素值。

4填空题
请补全代码,创建一个只包含整数5的元组并赋值给变量t:
t = ___
5填空题
已知集合 a = {1, 2, 3},b = {2, 3, 4},请计算它们的交集并赋值给变量 c:
c = ___