在 Python 中,除了常用的列表,元组(tuple)和集合(set)也是重要的数据结构 —— 元组是 “不可变的序列”,适合存储不希望被修改的数据;集合是 “无序的唯一元素集合”,擅长去重和关系运算。今天就系统讲解元组与集合的核心特性、常用操作,对比它们与列表的使用场景,最后用实战案例解决实际问题,让你按需选择合适的数据结构!
一、元组(tuple):不可变的 “稳定序列”
元组是一种有序且不可变的序列,和列表类似,能存储不同类型的元素,但一旦创建,元素就无法修改、添加或删除。这种 “不可变性” 让元组在存储固定数据(比如配置信息、坐标值)时更安全、高效。
1. 元组的定义与创建
元组用()包裹元素,元素之间用逗号分隔;创建单元素元组时,必须在元素后加逗号(避免和括号表达式混淆)。
常用创建方式:
| | | |
|---|
| t1 = (1, 2, "Python") | | (1, 2, 'Python') |
| t2 = (5,) | | (5,) |
| t3 = 3, "apple", True | | (3, 'apple', True) |
tuple() | t4 = tuple([1, 2, 3]) | | (1, 2, 3) |
实战示例:
\# 1. 多元素元组t1 = (10, 20, "数据")print("多元素元组:", t1) # 输出:(10, 20, '数据')print("元组类型:", type(t1)) # 输出:\<class 'tuple'>\# 2. 单元素元组(必须加逗号)t2 = (5,)t3 = 8, # 省略括号的单元素元组print("单元素元组t2:", t2, type(t2)) # 输出:(5,) \<class 'tuple'>print("单元素元组t3:", t3, type(t3)) # 输出:(8,) \<class 'tuple'>\# 3. 转换为元组list\_to\_tuple = tuple(\[1, 2, 3]) # 列表转元组str\_to\_tuple = tuple("Python") # 字符串转元组print("列表转元组:", list\_to\_tuple) # 输出:(1, 2, 3)print("字符串转元组:", str\_to\_tuple) # 输出:('P', 'y', 't', 'h', 'o', 'n')
2. 元组的核心特性:不可变性
不可变性是元组与列表最核心的区别 —— 元组创建后,元素的 “值” 和 “数量” 都无法修改,任何试图修改元组的操作都会报错。
不可变的体现(错误示例):
t = (1, 2, 3)\# 1. 试图修改元素值(报错)t\[0] = 10 # 报错:TypeError: 'tuple' object does not support item assignment\# 2. 试图添加元素(报错)t.append(4) # 报错:AttributeError: 'tuple' object has no attribute 'append'\# 3. 试图删除元素(报错)del t\[1] # 报错:TypeError: 'tuple' object doesn't support item deletion
特殊情况:元组内的可变元素
如果元组中包含可变元素(比如列表),该可变元素的 “内部内容” 可以修改,但元组本身的 “元素引用” 无法改变(即不能替换这个可变元素)。
\# 元组内包含列表(可变元素)t = (1, \[2, 3], 4)\# 修改元组内列表的内部元素(允许)t\[1]\[0] = 20print("修改后元组:", t) # 输出:(1, \[20, 3], 4)\# 试图替换元组内的列表(报错)t\[1] = \[5, 6] # 报错:TypeError: 'tuple' object does not support item assignment
3. 元组的常用操作
元组不支持修改类操作(如append()、remove()),但支持索引、切片、查找等 “只读” 操作,语法和列表基本一致。
(1)索引与切片(和列表完全相同)
t = (10, 20, 30, 40, 50)\# 索引:获取单个元素print("索引0:", t\[0]) # 输出:10print("反向索引-1:", t\[-1]) # 输出:50\# 切片:获取一段元素(返回新元组)print("切片1:4:", t\[1:4]) # 输出:(20, 30, 40)print("步长2:", t\[::2]) # 输出:(10, 30, 50)print("反转元组:", t\[::-1]) # 输出:(50, 40, 30, 20, 10)
(2)查找与统计(index()、count())
| | 示例(t = (10, 20, 30, 20, 40)) | |
|---|
index(元素) | 查找第一个匹配元素的索引,元素不存在则报错ValueError | t.index(20) | 1 |
index(元素, start) | | t.index(20, 2) | 3 |
count(元素) | | t.count(20) | 2 |
t = (10, 20, 30, 20, 40)print("第一个20的索引:", t.index(20)) # 输出:1print("从索引2开始找20的索引:", t.index(20, 2)) # 输出:3print("20出现的次数:", t.count(20)) # 输出:2print("50出现的次数:", t.count(50)) # 输出:0
(3)其他常用操作(len()、in/not in)
t = (10, 20, 30)\# len():获取元组长度print("元组长度:", len(t)) # 输出:3\# in:判断元素是否在元组中print("20是否在元组中:", 20 in t) # 输出:True\# not in:判断元素是否不在元组中print("40是否不在元组中:", 40 not in t) # 输出:True
二、集合(set):无序的 “唯一元素库”
集合是一种无序且元素唯一的容器,不允许有重复元素,擅长 “去重” 和 “关系运算”(如交集、并集、差集),适合处理数据去重、数据间关系判断等场景。
1. 集合的定义与创建
集合用{}包裹元素(注意:空集合不能用{},需用set(),因为{}会被识别为字典),元素之间用逗号分隔;也可以用set()函数将可迭代对象转为集合(自动去重)。
常用创建方式:
| | | |
|---|
| s1 = {1, 2, 3, 3} | | {1, 2, 3} |
set() | s2 = set() | | set() |
set() | s3 = set([1, 2, 2, 3]) | | {1, 2, 3} |
实战示例:
\# 1. 直接创建集合(自动去重)s1 = {10, 20, 20, 30}print("去重后的集合:", s1) # 输出:{10, 20, 30}(顺序可能不同,因为集合无序)print("集合类型:", type(s1)) # 输出:\<class 'set'>\# 2. 创建空集合(必须用set())s2 = set()s3 = {} # 这是字典,不是集合print("空集合s2:", s2, type(s2)) # 输出:set() \<class 'set'>print("s3的类型:", type(s3)) # 输出:\<class 'dict'>\# 3. 转换为集合(自动去重)list\_to\_set = set(\[1, 2, 2, 3]) # 列表转集合(去重)str\_to\_set = set("aabbcc") # 字符串转集合(去重,每个字符唯一)print("列表转集合:", list\_to\_set) # 输出:{1, 2, 3}print("字符串转集合:", str\_to\_set) # 输出:{'a', 'b', 'c'}
注意:
集合是 “无序” 的,元素没有固定位置,不能通过索引访问(比如s1[0]会报错),只能通过in/not in判断元素是否存在,或通过循环遍历。
2. 集合的核心特性:无序性与唯一性
(1)无序性:元素没有固定顺序,每次打印顺序可能不同
s = {1, 2, 3}print(s) # 可能输出{1, 2, 3},也可能输出{2, 1, 3}等,顺序不固定
(2)唯一性:不允许重复元素,添加重复元素会被自动忽略
s = {1, 2, 3}s.add(3) # 添加重复元素,无效果print(s) # 输出:{1, 2, 3}
3. 集合的交、并、补、差运算
集合的核心价值之一是 “关系运算”,可以快速计算两个集合的交集(共同元素)、并集(所有元素)、差集(独有元素)、对称差集(非共同元素),支持运算符和方法两种调用方式。
假设两个集合:s1 = {1, 2, 3, 4},s2 = {3, 4, 5, 6}
| | | | | |
|---|
| & | s1.intersection(s2) | | s1 & s2 | {3, 4} |
| | | s1.union(s2) | | |
| - | s1.difference(s2) | | s1 - s2 | {1, 2} |
| ^ | s1.symmetric_difference(s2) | | s1 ^ s2;s1.symmetric_difference(s2) | {1, 2, 5, 6} |
| < | s1.issubset(s2) | 判断 s1 是否是 s2 的子集(s1 所有元素在 s2 中) | {1,2} < s2 | False |
| > | s1.issuperset(s2) | 判断 s1 是否是 s2 的超集(s2 所有元素在 s1 中) | s1 > {1,2} | True |
实战示例:
s1 = {1, 2, 3, 4}s2 = {3, 4, 5, 6}\# 1. 交集print("交集(&):", s1 & s2) # 输出:{3, 4}print("交集(方法):", s1.intersection(s2)) # 输出:{3, 4}\# 2. 并集print("并集(|):", s1 | s2) # 输出:{1, 2, 3, 4, 5, 6}print("并集(方法):", s1.union(s2)) # 输出:{1, 2, 3, 4, 5, 6}\# 3. 差集(s1 - s2:s1独有元素)print("差集(-):", s1 - s2) # 输出:{1, 2}print("差集(方法):", s1.difference(s2)) # 输出:{1, 2}\# 4. 对称差集print("对称差集(^):", s1 ^ s2) # 输出:{1, 2, 5, 6}print("对称差集(方法):", s1.symmetric\_difference(s2)) # 输出:{1, 2, 5, 6}\# 5. 子集判断print("{3,4}是s2的子集:", {3,4} < s2) # 输出:Trueprint("s1是s2的子集:", s1.issubset(s2)) # 输出:False
4. 集合的常用方法
集合支持添加、删除元素等操作,常用方法如下(假设集合s = {1, 2, 3}):
| | | |
|---|
add(元素) | | s.add(4) | {1, 2, 3, 4} |
remove(元素) | 从集合中删除指定元素,元素不存在则报错KeyError | s.remove(3) | {1, 2} |
discard(元素) | 从集合中删除指定元素,元素不存在则不报错(推荐,比 remove () 安全) | s.discard(4) | {1, 2, 3} |
pop() | | | |