在前面几章中,我们使用的变量一次只能存储一个数据(如 age = 18 或 name = "小明")。
但在现实开发中,我们往往需要处理海量的组合数据:
如果为每个数据都单独创建一个变量,代码将变得无法维护。Python 提供了四种强大的内置组合数据类型(也称容器):列表 (list)、元组 (tuple)、字典 (dict) 与 集合 (set)。
完成本章学习后,你将能够:
掌握列表 (list) 的创建、正逆向索引、切片与常用增删改查方法;
理解元组 (tuple) 的不可变特性与其应用场景;
掌握字典 (dict) 的键值对结构、.get() 查键安全机制与遍历方法;
掌握集合 (set) 的自动去重功能与交并集运算;
理解 Python 变量引用机制,准确区分浅拷贝与深拷贝的不同行为。
列表是 Python 中最常用、最灵活的数据结构。它可以存储任意类型的数据,并且内容和长度都可以随时修改。
列表使用方括号 [] 定义,元素之间用逗号分隔。列表中每一个元素都有对应的数字位置编号,称为索引 (Index)。
Python 同时支持正向索引与逆向索引:
0 开始递增;-1 开始递减(最后一个元素索引为 -1)。假设存在列表 letters = ["A", "B", "C", "D", "E", "F"],其双向索引结构如下:
| 元素内容 | "A" | "B" | "C" | "D" | "E" | "F" |
|---|---|---|---|---|---|---|
| 正向索引 | 0 | 1 | 2 | 3 | 4 | 5 |
| 逆向索引 | -6 | -5 | -4 | -3 | -2 | -1 |
letters = ["A", "B", "C", "D", "E", "F"]
print(letters[0]) # 输出: "A" (第 1 个元素)
print(letters[3]) # 输出: "D" (第 4 个元素)
print(letters[-1]) # 输出: "F" (最后一个元素)
print(letters[-2]) # 输出: "E" (倒数第 2 个元素)
如果需要从列表中提取一段子列表,可以使用切片 (Slicing) 语法。
点击放大查看图:Python 列表切片索引图解
列表变量[start : stop : step]
start (起始索引):包含该位置的元素(包头);如果省略,正向步长时默认从 0 开始;stop (终止索引):不包含该位置的元素(不包尾),实际切到 stop - 1 为止;如果省略,默认切到列表末尾;step (步长与方向):表示每次移动的跨度与方向,默认为 1。letters = ["A", "B", "C", "D", "E", "F"]
# 模式 A: 指定范围切片 (包头不包尾)
print(letters[1:4]) # 输出: ['B', 'C', 'D'] (提取索引 1, 2, 3)
# 模式 B: 省略参数快捷写法
print(letters[:3]) # 输出: ['A', 'B', 'C'] (提取前 3 个元素)
print(letters[3:]) # 输出: ['D', 'E', 'F'] (从索引 3 一直提取到末尾)
print(letters[:]) # 输出原列表的全量浅拷贝副本
# 模式 C: 步长跨进 (step > 1)
print(letters[::2]) # 输出: ['A', 'C', 'E'] (从头到尾,每隔 1 个元素取 1 个)
print(letters[1::2]) # 输出: ['B', 'D', 'F'] (从索引 1 开始,提取奇数位置元素)
# 模式 D: 负步长反向切片与反转 (step < 0)
print(letters[::-1]) # 输出: ['F', 'E', 'D', 'C', 'B', 'A'] (快速反转整个列表)
print(letters[4:1:-1]) # 输出: ['E', 'D', 'C'] (从索引 4 倒序切到索引 2)
在 Python 中,单元素索引与切片对越界处理有本质不同:
letters = ["A", "B", "C"]
# 单元素索引越界:直接抛出 IndexError 报错
# print(letters[10]) # 报错: IndexError: list index out of range
# 切片越界:非常宽容,不会报错,会自动截断至列表实际边界
print(letters[1:100]) # 输出: ['B', 'C'] (自动截断到列表末尾)
| 方法名 | 功能说明 | 代码示例 |
|---|---|---|
append(x) | 在列表末尾追加一个元素 | fruits.append("葡萄") |
insert(i, x) | 在指定索引 i 处插入元素 | fruits.insert(1, "芒果") |
pop(i) | 弹出并返回指定索引的元素(默认最后一个) | removed = fruits.pop() |
remove(x) | 删除列表中第一个值为 x 的元素 | fruits.remove("香蕉") |
sort() | 对列表进行原地排序(默认升序) | numbers.sort() |
len(seq) | 获取列表中元素的总个数 | print(len(fruits)) |
scores = [88, 72, 95, 60]
scores.sort(reverse=True) # 降序排序
print(scores) # 输出: [95, 88, 72, 60]
元组使用圆括号 () 定义。元组与列表非常相似,唯一的区别是:元组一旦创建,其内部元素绝不能被修改、增加或删除。
# 1. 定义元组
point = (10, 20)
server_config = ("127.0.0.1", 8080)
# 2. 读取元素
print(point[0]) # 输出: 10
# 3. 尝试修改元组元素会引发 TypeError 报错!
# point[0] = 15 # 报错: 'tuple' object does not support item assignment
# 元组解包
x, y = point
print(f"X 坐标: {x}, Y 坐标: {y}")
当我们需要通过一个唯一的标识(键 Key)去快速查找对应的数据(值 Value)时,字典是最佳选择。
字典使用花括号 {} 定义,每个键值对用 键: 值 表示,对之间用逗号分隔。
# 创建学生档案字典
student = {
"name": "小明",
"age": 18,
"score": 92.5
}
# 1. 通过 键(Key) 查找 值(Value)
print(student["name"]) # 输出: 小明
# 2. 修改与新增键值对
student["score"] = 95.0 # 修改已有键的值
student["city"] = "北京" # 新增不存在的键值对
.get() 方法如果直接使用 student["gender"] 查找不存在的键,会直接引发 KeyError 程序崩溃。推荐使用 .get() 方法进行安全查找:
# 使用 .get(key, default) 查找:若键不存在,返回默认值 None 或自定义替代值
gender = student.get("gender", "未知")
print(gender) # 输出: 未知(程序不会报错崩溃)
# 遍历所有的 键 与 值
for key, value in student.items():
print(f"{key}: {value}")
集合使用花括号 {} 定义(只有元素,没有键值对)。集合最大的两大特点是:元素无序 且 自动去重。
raw_user_ids = [101, 102, 101, 103, 102, 104]
# 将列表转换为集合,自动过滤重复数据
unique_ids = set(raw_user_ids)
print(unique_ids) # 输出: {101, 102, 103, 104}
math_class = {"小明", "小红", "小刚"}
english_class = {"小红", "小华", "小刚"}
# 1. 交集 & (同时参加两门课的人)
print(math_class & english_class) # 输出: {'小红', '小刚'}
# 2. 并集 | (参加了至少一门课的人)
print(math_class | english_class) # 输出: {'小明', '小红', '小刚', '小华'}
点击放大查看图:Python 4 大容器选择指南图
为了帮助你更形象地理解列表、元组、字典与集合的内部存储结构,请体验下方的交互展示组件:
Data Structure Lab
有序、可修改、允许重复
列表保留顺序和重复项,可以通过索引修改。
在处理组合数据类型时,理解 Python 的内存引用与拷贝机制至关重要。
点击放大查看图:Python 赋值、浅拷贝与深拷贝关系图
Copy Lab
original = [[1, 2]]原对象
副本 = [[1, 2]]内层对象共享
两个变量指向同一个对象,修改任意一方都会影响另一方。
id() 内存地址在 Python 中,变量名本质上只是指向内存中具体对象的“标签”。使用内置函数 id() 可以查询对象在内存中的实际物理地址:
list_a = [1, 2, 3]
list_b = list_a # 直接赋值:共享同一个内存地址
print(id(list_a))
print(id(list_b)) # 地址完全相同!
list_b.append(4)
print(list_a) # 输出: [1, 2, 3, 4] (修改 list_b 会同步改变 list_a)
copy()):创建一个新的容器对象,但只复制外层。如果内部嵌套了子列表,子列表依然共享内存;copy.deepcopy()):递归复制外层与所有嵌套层级,完全独立。import copy
original = [1, [2, 3]]
# 浅拷贝
shallow = original.copy()
# 深拷贝
deep = copy.deepcopy(original)
original[1].append(99)
print(shallow) # 输出: [1, [2, 3, 99]] (受外层原列表修改影响)
print(deep) # 输出: [1, [2, 3]] (完全独立,不受影响)
请在下方的代码块中,点击 “运行代码”,体验列表与字典组合使用的复杂数据管理:
| 错误现象 / 异常类型 | 常见原因 | 标准排查与处理方式 |
|---|---|---|
IndexError: list index out of range | 访问了不存在的列表索引(如列表只有 3 个元素,却访问了 list[3]) | 记住索引从 0 开始,最大合法索引为 len(list) - 1 |
KeyError: 'xxx' | 尝试直接用中括号 dict['xxx'] 访问字典中不存在的键 | 改用安全查询方法 dict.get('xxx', default_val) 防崩溃 |
TypeError: 'tuple' object does not support item assignment | 尝试给元组中的元素重新赋值 | 元组不可变,如需频繁修改数据,请改用列表 list |
| 修改变量 A 导致变量 B 意外被改变 | 使用了直接赋值 b = a 导致两个变量共享同一内存引用 | 如果需要独立副本,请使用 a.copy() 浅拷贝或 copy.deepcopy(a) 深拷贝 |
[] 创建列表,并使用正逆向索引与切片提取元素;append()、pop()、remove() 与 sort() 管理列表;() 的不可变特性,并能使用元组解包给多变量赋值;{} 的键值对结构,并能用 .get() 方法安全查询键;set() 具有自动去重特性,并能进行交集 & 与并集 | 计算;copy() 与深拷贝 deepcopy() 在处理嵌套结构时的根本区别。共 4 道精选测试题 · 答题进度已自动保存
恭喜你掌握了 Python 处理海量数据的四大容器!
下一步我们将深入学习 05. 函数与作用域,掌握如何将重复的代码打包成模块化的函数,实现代码的高效复用。