一、四种核心数据结构
| 数据结构 | 语法标识 | 核心特点 | 典型使用场景 | 是否支持嵌套 |
|---|---|---|---|---|
| 列表(list) | [] | 有序、可变(可增删改) | 存储一组有序、需要修改的同类数据(如测试用例列表) | ✅ 支持 |
| 元组(tuple) | () | 有序、不可变(定义后不能改) | 存储一组有序、无需修改的固定数据(如坐标、键值对) | ✅ 支持 |
| 集合(set) | {}(无键值) | 无序、无重复、可变 | 去重、集合运算(如找两个列表的交集/差集) | ✅ 支持 |
| 字典(dict) | {}(键值对) | 无序、键唯一、值可变 | 存储"键-值"映射关系(如用户信息、配置项) | ✅ 支持 |
二、例子拆解:[("3+5", 8), ("2+4", 6), ("6*9", 42)]
这个例子是列表嵌套元组,先拆结构:
- 最外层
[]→ 列表:因为需要存储 "多组测试用例",后续可能要新增 / 删除用例(可变); - 内层
()→ 元组:每组用例是 "表达式 + 预期结果",这组数据是固定的(比如 "3+5" 的预期结果就是 8,不需要修改),用元组更安全(防止误改)。
为什么要嵌套?
嵌套的核心是 "分组管理数据"------ 就像用文件夹装文件:
- 列表是 "测试用例文件夹",里面装多个 "用例文件";
- 元组是每个 "用例文件",里面装 "表达式" 和 "预期结果" 两个关联数据。如果不嵌套,写成
["3+5", 8, "2+4", 6],就无法区分 "哪个表达式对应哪个结果",数据会乱。
三、每种结构的实操示例
1. 列表(list):存储可修改的有序数据
# 我的例子:列表存储多组测试用例
test_cases = [("3+5", 8), ("2+4", 6), ("6*9", 42)]
# 新增一个用例(列表可变)
test_cases.append(("10-3", 7))
print(test_cases) # 输出:[("3+5", 8), ("2+4", 6), ("6*9", 42), ("10-3", 7)]
2. 元组(tuple):存储不可修改的固定数据
# 单组用例:表达式+预期结果(固定不变)
case1 = ("3+5", 8)
# 尝试修改元组(会报错,体现"不可变"特性)
# case1[1] = 9 # 报错:TypeError: 'tuple' object does not support item assignment
3. 集合(set):去重 / 集合运算
# 比如给测试用例去重
duplicate_cases = [("3+5", 8), ("3+5", 8), ("2+4", 6)]
unique_cases = set(duplicate_cases) # 转集合自动去重
print(unique_cases) # 输出:{('3+5', 8), ('2+4', 6)}
4. 字典(dict):键值映射(更易读的测试用例)
# 把上述例子改成字典(键是表达式,值是预期结果)
test_cases_dict = {"3+5": 8, "2+4": 6, "6*9": 42}
# 取值更直观:通过"表达式"找"预期结果"
print(test_cases_dict["3+5"]) # 输出:8
# 修改值(字典可变)
test_cases_dict["6*9"] = 54 # 修正错误的预期结果
四、嵌套的核心逻辑(为什么能嵌套?)
Python 数据结构的嵌套,本质是 "一个容器里可以装另一个容器",只要符合语法规则即可:
- 列表里可以嵌套元组 / 列表 / 字典 / 集合(比如上述例子:列表套元组);
- 元组里可以嵌套列表 / 元组(但嵌套的列表仍可变,比如
(1, [2,3]),能改[2,3]为[4,5]); - 字典的值可以是任意结构(键只能是不可变类型:字符串 / 数字 / 元组);
- 集合里只能装不可变类型(所以集合不能嵌套集合,但能嵌套元组)。
| 特性层面 | 具体说明 | 示例((1, [2,3])) |
|---|---|---|
| 元组的不可变 | 元组里的元素"位置"和"指向"不能改:比如不能把第2个元素从列表[2,3]换成字符串"abc" | ✅ 错误:t[1] = "abc"(直接报错) ❌ 正确:t[1][0] = 4(修改列表内部) |
| 嵌套列表的可变 | 元组只是"装着"列表,列表本身是可变对象,所以列表内部的内容能改 | t = (1, [2,3]) t[1][0] = 4 → t变成(1, [4,3]) |
为什么这种 "半可变" 的元组依然有价值?
元组的核心意义不在于 "绝对的不可变",而在于 「结构稳定 + 语义明确 + 场景适配」,哪怕嵌套了可变元素,依然有不可替代的作用:
1. 核心价值 1:元组的 "结构" 永远稳定(最关键)
比如定义 t = (1, [2,3]),不管里面的列表怎么改,元组永远是「2 个元素」:第 1 个是数字 1,第 2 个是列表。
- 不能新增元素(
t.append(4)报错); - 不能删除元素(
del t[0]报错); - 不能替换元素(
t[1] = 5报错)。
这种 "结构固定" 的特性,在很多场景下至关重要:
# 场景:存储用户的"ID + 爱好列表",ID不能改,爱好可以加
user_info = (1001, ["看书", "跑步"])
# ✅ 允许:给用户加新爱好(列表内部改)
user_info[1].append("游泳")
# ❌ 禁止:把用户ID从1001改成1002(元组结构不能改)
# user_info[0] = 1002 # 报错,避免误改核心信息
2. 核心价值 2:语义上的 "固定组合"(可读性 + 规范性)
元组天生用来表示「一组有固定含义的关联数据」,比如:
- 坐标:
(x, y)(x/y 的位置不能换,x 可以是数字,y 哪怕是可变的列表,也不影响 "坐标是两个值" 的语义); - 测试用例:
("3+5", 8)(表达式和结果的组合不能拆、不能换位置); - 函数返回多值:
return (code, data)(code 是状态码,data 是可变的字典,结构固定更易读)。
如果用列表代替,别人看代码时无法区分 "这个列表是可随意增删的" 还是 "是固定组合的",而元组的语义就是「这是一组固定搭配的数」。
3. 核心价值 3:适配 Python 的特殊场景(列表替代不了)
元组的 "不可变" 特性让它能适配很多列表用不了的场景,哪怕嵌套了可变元素:
-
可以作为字典的键(列表不行):
用元组做键:存储"用户ID+月份"对应的消费数据
consume_data = {(1001, "2026-01"): [100, 200]}
列表不能做键:list作为键会报错
consume_data[[1001, "2026-01"]] = [100, 200] # 报错
-
可以放入集合(列表不行):
存储多组用户信息,自动去重(元组可哈希,列表不可)
user_set = {(1001, ["看书"]), (1002, ["跑步"])}
-
性能更好:元组的内存占用更小,遍历速度更快(因为 Python 不用为 "增删改" 预留内存)。
元组里嵌套可变元素,看似 "矛盾",实则是 「分层控制可变范围」:
- 元组保证「整体结构」不可变(核心信息不丢、不错位);
- 嵌套的可变元素(列表)保证「局部内容」可灵活修改(非核心信息可调整)。
这种设计让元组既能满足 "结构稳定" 的需求,又能兼容 "局部灵活" 的场景,这正是它比列表更灵活的地方 ------ 比如存储 "测试用例(表达式 + 结果)",用元组能保证 "每个用例只有两个元素,表达式在前、结果在后",哪怕结果是个可变的列表(比如多组预期值),也不会打乱用例的整体结构。
总结
- 选结构的核心原则:需要 "有序可改" 用列表,"固定不改" 用元组,"去重 / 集合运算" 用集合,"键值映射(易读)" 用字典;
- 嵌套的目的:分组管理关联数据(比如 "测试用例列表" 里装 "每组用例的元组");
- 上述例子用 "列表套元组",是因为要存储 "多组可新增的用例"(列表),且每组用例 "表达式 + 结果" 固定不变(元组)。