第4章 容器类型详解¶
本章目标:系统掌握 列表、元组、字典、集合 四类容器。
容器类型简介¶
在 Python 中,容器类型(Container Types) 是用于存储多个数据项的数据结构。
它们允许我们方便地组织、访问和修改数据。
Python 的四种主要容器类型:
| 类型 | 名称 | 是否可变 | 是否有序 | 是否允许重复 |
|---|---|---|---|---|
| list | 列表 | ✅ 是 | ✅ 是 | ✅ 是 |
| tuple | 元组 | ❌ 否 | ✅ 是 | ✅ 是 |
| dict | 字典 | ✅ 是 | ✅ 键无序(Python 3.7+保持插入顺序) | ❌ 键唯一 |
| set | 集合 | ✅ 是 | ❌ 否 | ❌ 否 |
一、 列表(list)¶
1. 创建列表¶
在 Python 中,列表是由一系列元素按特定顺序构成的可变序列。使用 [] 字面量语法定义,多个元素之间用逗号分隔。
items1 = [35, 12, 99, 68, 55, 35, 87]
items2 = ['Python', 'Java', 'Go', 'Kotlin']
items3 = [100, 12.3, 'Python', True]
print(items1) # [35, 12, 99, 68, 55, 35, 87]
print(items2) # ['Python', 'Java', 'Go', 'Kotlin']
print(items3) # [100, 12.3, 'Python', True]
- 列表可包含重复元素(如
items1中的35)。 - 列表可包含不同类型的元素(如
items3),但不推荐:后续操作会变复杂。 - 可用
type()查看类型;对“容器”变量,习惯用复数名词命名(如items)。
也可用 list() 构造器将其他序列转换为列表:
items4 = list(range(1, 10)) # 1~9
items5 = list('hello') # 按字符拆分成列表
print(items4) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(items5) # ['h', 'e', 'l', 'l', 'o']
2. 列表的运算¶
- 拼接与重复
items5 = [35, 12, 99, 45, 66]
items6 = [45, 58, 29]
items7 = ['Python', 'Java', 'JavaScript']
print(items5 + items6)
# [35, 12, 99, 45, 66, 45, 58, 29]
print(items6 * 3)
# [45, 58, 29, 45, 58, 29, 45, 58, 29]
print(items7 * 2)
# ['Python', 'Java', 'JavaScript', 'Python', 'Java', 'JavaScript']
items5 += items6 # 原地扩展(等同于 extend)
print(items5)
# [35, 12, 99, 45, 66, 45, 58, 29]
- 成员测试
print(29 in items6) # True
print(99 in items6) # False
print('C++' not in items7) # True
print('Python' in items7) # True
3. 索引与切片¶
- 索引(Indexing):
0 ~ N-1正向索引;-1 ~ -N反向索引。 - 切片(Slicing):
seq[start:end:step],左闭右开;step可为负数。
items8 = ['apple', 'waxberry', 'pitaya', 'peach', 'watermelon']
print(items8[0]) # apple(第1个)
print(items8[2]) # pitaya(第3个)
print(items8[-1]) # watermelon(最后1个)
# 修改指定位置元素(列表可变的体现)
items8[2] = 'durian'
print(items8) # ['apple', 'waxberry', 'durian', 'peach', 'watermelon']
# 切片:左闭右开;步长为正从左向右,为负从右向左
print(items8[1:3:1]) # ['waxberry', 'durian']
print(items8[0:5:2]) # ['apple', 'durian', 'watermelon']
print(items8[-2:-6:-1]) # ['peach', 'durian', 'waxberry', 'apple']
# 省略写法:start=0 可省;end=N 可省;step=1 可省
print(items8[1:3]) # ['waxberry', 'durian']
print(items8[:3]) # ['apple', 'waxberry', 'durian']
print(items8[::2]) # ['apple', 'durian', 'watermelon']
# 切片赋值:批量替换
items8[1:3] = ['x', 'o']
print(items8) # ['apple', 'x', 'o', 'peach', 'watermelon']
⚠️ 索引越界会触发
IndexError: list index out of range。
4. 关系比较¶
nums1 = [1, 2, 3, 4]
nums2 = list(range(1, 5))
nums3 = [3, 2, 1]
print(nums1 == nums2) # True(对应元素完全相同)
print(nums1 != nums2) # False
print(nums1 <= nums3) # True(逐元素字典序比较)
print(nums2 >= nums3) # False
实务中较少直接比较两个列表大小,必要时请明确排序规则与 key。
5. 遍历列表的两种方式¶
languages = ['Python', 'Java', 'C++', 'Kotlin']
# 方式一:通过索引遍历
for i in range(len(languages)):
print(languages[i])
# 方式二:直接迭代元素(推荐)
for lang in languages:
print(lang)
6. 列表常用方法(增删改查)¶
languages = ['Python', 'Java', 'C++']
# 追加到末尾
languages.append('JavaScript')
# 按位置插入
languages.insert(1, 'SQL')
# 删除指定值(若不存在将抛 ValueError)
if 'Java' in languages:
languages.remove('Java')
# 弹出最后/指定位置的元素(并返回该元素)
last = languages.pop() # 默认最后一个
second = languages.pop(1)
# 清空
languages.clear()
del 列表[索引]:按索引删除(不返回被删元素,速度略优于pop(索引))。index(value, start=..., end=...):找元素位置,不存在抛ValueError。count(value):统计出现次数。
items = ['Python', 'Java', 'Java', 'C++', 'Kotlin', 'Python']
print(items.index('Python')) # 0
print(items.index('Python', 1)) # 5(从索引1开始找)
print(items.count('Python')) # 2
print(items.count('Swift')) # 0
- 排序与反转
items = ['Python', 'Java', 'C++', 'Kotlin', 'Swift']
items.sort() # 原地升序
items.reverse() # 原地反转
# 不改变原列表,返回新列表
sorted_items = sorted(items, key=len, reverse=True) # 按长度降序
list.sort()原地、返回None;sorted(iterable, key=..., reverse=...)返回新列表。
7. 列表生成式(推导式)¶
强烈建议:优先用生成式 构造新列表。
# 1~99 中能被 3 或 5 整除的数
items = [i for i in range(1, 100) if i % 3 == 0 or i % 5 == 0]
# 平方列表
nums1 = [35, 12, 97, 64, 55]
nums2 = [n ** 2 for n in nums1]
# 过滤大于 50 的元素
nums3 = [n for n in nums1 if n > 50]
- 解释器对推导式有专门优化的字节码(如
LIST_APPEND),通常 比for+append更快。
8. 嵌套列表¶
# 5个学生,3门课
scores = [[95, 83, 92], [80, 75, 82], [92, 97, 90], [80, 78, 69], [65, 66, 89]]
print(scores[0]) # 第1个学生的全部成绩
print(scores[0][1]) # 第1个学生第2门课成绩
# 键盘录入 5×3
scores = []
for _ in range(5):
row = []
for _ in range(3):
row.append(int(input('请输入: ')))
scores.append(row)
# 随机生成 5×3
import random
scores = [[random.randrange(60, 101) for _ in range(3)] for _ in range(5)]
二、 元组(tuple)¶
1. 创建与特性¶
colors = ('red', 'green', 'blue')
single = ('one',) # 单元素元组必须加逗号
empty = ()
print(type(single)) # <class 'tuple'>
- 不可变:创建后不能增删改(可提升可哈希性/安全性)。
- 有序、可索引/切片、可迭代、可嵌套。
2. 典型用法¶
# 解包赋值
person = ('Alice', 25, 'Tokyo')
name, age, city = person
# 元组作字典键(元素需可哈希)
d = {('Tokyo', 'JP'): 37_000_000}
三、 字典(dict)¶
1. 创建与访问¶
person = {"name": "Tom", "age": 28, "city": "Osaka"}
print(person["name"]) # 不存在 -> KeyError
print(person.get("city")) # 推荐:不存在 -> None 或给默认
print(person.get("city", "N/A"))
- 键唯一,常用不可变类型(str、int、tuple)。
- Python 3.7+ 保持插入顺序。
2. 修改 / 新增 / 删除¶
person["age"] = 30
person["gender"] = "Male"
del person["city"] # 或 person.pop("city", None)
3. 遍历 / 推导式 / 排序视图¶
for k in person.keys(): print(k)
for v in person.values(): print(v)
for k, v in person.items(): print(k, v)
# 推导式:反转映射
m = {"a": 1, "b": 2, "c": 3}
inv = {v: k for k, v in m.items()}
# 排序视图
scores = {"Tom": 85, "Alice": 90, "Bob": 78}
print(sorted(scores)) # 键排序
print(sorted(scores.items(), key=lambda kv: kv[1], reverse=True))
四、 集合(set)¶
1. 创建¶
numbers = {1, 2, 3, 3, 2}
print(numbers) # {1, 2, 3}
empty = set() # 空集合必须用 set()
- 无序、唯一、可变(
frozenset为不可变集合)。 - 不支持索引;适合去重、判重、集合代数。
2. 集合运算 / 修改 / 推导式¶
a, b = {1, 2, 3, 4}, {3, 4, 5, 6}
print(a | b, a & b, a - b, a ^ b)
s = {1, 2, 3}
s.add(4); s.update([5, 6])
s.remove(2) # 不存在抛 KeyError
s.discard(10) # 不存在不报错
nums = [1, 2, 2, 3, 3, 3]
uniq = {x for x in nums if x % 2 == 1} # {1, 3}
五、 容器共通的“坑位与技巧”¶
# 浅拷贝 vs 深拷贝
import copy
a = [[1, 2], [3, 4]]
b = a.copy()
c = copy.deepcopy(a)
a[0][0] = 9
print(b) # [[9, 2], [3, 4]](共享内层)
print(c) # [[1, 2], [3, 4]](完全独立)
# 稳定排序 + key 组合
students = [
{"name": "Tom", "age": 20, "score": 85},
{"name": "Alice", "age": 20, "score": 90},
{"name": "Bob", "age": 22, "score": 78},
]
students.sort(key=lambda s: s["score"], reverse=True)
students.sort(key=lambda s: s["age"])
# 星号解包
head, *middle, tail = [10, 20, 30, 40, 50]
print(head, middle, tail) # 10 [20, 30, 40] 50
六、 容器选择建议¶
| 需求 | 推荐容器 | 理由 |
|---|---|---|
| 需要按序访问且会修改 | list | 有序、可变、API 完整 |
| 固定结构只读数据 | tuple | 不可变、更安全、可作键 |
| 键值映射、查找频繁 | dict | O(1) 平均查找、业务表达强 |
| 去重、集合代数运算 | set | 自动去重、并交差运算高效 |
七、 小结¶
list:最常用的可变序列,熟练掌握切片、增删改查、生成式与排序。tuple:不可变序列,适于只读与解包,必要时可作字典键。dict:业务最常用的数据结构,掌握增删改查、遍历、推导式、排序视图。set:去重与集合运算的首选,记住并/交/差/对称差与增删方法。- 理解浅深拷贝、排序稳定性、解包技巧,能显著减少 Bug。