四种容器,四种性格:列表能改、元组锁定、字典查得快、集合专治重复。选对了容器,代码就顺了一半。
单个变量只能存一个值。要存一批数据,就需要容器。Python 内置的四种容器各有各的脾气,这一篇一次讲完。
你将学到
- 列表的增删改查与切片,
sort与sorted的区别 - 元组与解包,为什么需要不可变的序列
- 字典的键值操作,
get/items/keys/values与遍历 - 集合的去重与交并差
- 四种容器怎么选
- 嵌套数据结构初识
前置知识
先读 上一篇:字符串与格式化。
列表 list:有序、可变
fruits = ["苹果", "香蕉", "橘子"]
print(fruits[0]) # 输出: 苹果
print(fruits[-1]) # 输出: 橘子
print(len(fruits)) # 输出: 3
增:append / insert / extend
fruits = ["苹果", "香蕉"]
fruits.append("橘子") # 末尾追加一个
fruits.insert(0, "西瓜") # 在下标 0 处插入
fruits.extend(["葡萄", "梨"]) # 一次追加多个
print(fruits)
# 输出: ['西瓜', '苹果', '香蕉', '橘子', '葡萄', '梨']
删:remove / pop / del
nums = [10, 20, 30, 20]
nums.remove(20) # 删除第一个值为 20 的元素
print(nums) # 输出: [10, 30, 20]
last = nums.pop() # 弹出最后一个,并返回它
print(last, nums) # 输出: 20 [10, 30]
del nums[0] # 用 del 按下标删
print(nums) # 输出: [30]
改:直接赋值 + 切片赋值
nums = [1, 2, 3, 4, 5]
nums[0] = 100
nums[1:3] = [20, 30, 40] # 切片赋值,长度可以不同
print(nums) # 输出: [100, 20, 30, 40, 4, 5]
查:索引、切片、in
nums = [10, 20, 30, 40, 50]
print(nums[1:4]) # 输出: [20, 30, 40]
print(30 in nums) # 输出: True
print(nums.index(30)) # 输出: 2
排序:sort 与 sorted(最易混的一对)
nums = [3, 1, 4, 1, 5]
nums.sort() # 原地排序,返回 None,会改原列表
print(nums) # 输出: [1, 1, 3, 4, 5]
nums = [3, 1, 4, 1, 5]
new = sorted(nums) # 返回新列表,原列表不动
print(new, nums) # 输出: [1, 1, 3, 4, 5] [3, 1, 4, 1, 5]
print(sorted(nums, reverse=True)) # 输出: [5, 4, 3, 1, 1]
print(sorted(["bb", "a", "ccc"], key=len)) # 按长度排: ['a', 'bb', 'ccc']
元组 tuple:有序、不可变
元组用小括号,一旦创建就不能改。
point = (3, 4)
print(point[0], len(point)) # 输出: 3 2
# point[0] = 10 # ❌ 不可变,TypeError
单元素元组要加逗号,否则只是个带括号的值:
t1 = (5) # 这是整数 5,不是元组!
t2 = (5,) # ✅ 这才是单元素元组
print(type(t1), type(t2)) # 输出: <class 'int'> <class 'tuple'>
解包
point = (3, 4)
x, y = point
print(x, y) # 输出: 3 4
def min_max(nums):
return min(nums), max(nums) # 函数返回多值其实就是返回元组
low, high = min_max([5, 2, 8])
print(low, high) # 输出: 2 8
first, *rest = [1, 2, 3, 4] # 用 * 收集剩余
print(first, rest) # 输出: 1 [2, 3, 4]
为什么需要不可变
- 可以安全地作为字典的键、集合的元素。
- 多线程共享时不用担心被改。
- 语义上表达"这组值不该变",比如坐标、RGB 颜色。
locations = {(0, 0): "原点", (1, 2): "点A"} # 元组能当字典的键
print(locations[(0, 0)]) # 输出: 原点
字典 dict:键值对
字典存"键 → 值"的映射,查起来很快。
person = {"name": "小明", "age": 18, "city": "北京"}
print(person["name"], len(person)) # 输出: 小明 3
增删改
person = {"name": "小明"}
person["age"] = 18 # 新增键
person["name"] = "小明同学" # 修改已有的键
del person["age"] # 删除键
print(person) # 输出: {'name': '小明同学'}
get:安全取值
用 [] 取不存在的键会报 KeyError,get 不会:
person = {"name": "小明"}
# print(person["age"]) # ❌ KeyError: 'age'
print(person.get("age", 0)) # 输出: 0 不存在时给默认值
实践建议:不确定键存不存在时,用 get。
遍历与判断
person = {"name": "小明", "age": 18}
print(list(person.keys()), list(person.values())) # 输出: ['name', 'age'] ['小明', 18]
for key, value in person.items(): # 同时拿到键和值(最常用)
print(f"{key} = {value}")
# 输出: name = 小明 / age = 18
print("name" in person) # 输出: True
# 字典合并(Python 3.9+)
print({"x": 1} | {"y": 2}) # 输出: {'x': 1, 'y': 2}
集合 set:无序、不重复
集合最擅长两件事:去重和做集合运算。
nums = [1, 2, 2, 3, 3, 3]
print(set(nums)) # 输出: {1, 2, 3}(顺序可能不同,集合无序)
添加与删除
s = {1, 2, 3}
s.add(4)
print(s) # 输出: {1, 2, 3, 4}
s.discard(2) # 删除,不存在也不报错
print(s) # 输出: {1, 3, 4}
交、并、差
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a & b) # 输出: {3, 4} 交集
print(a | b) # 输出: {1, 2, 3, 4, 5, 6} 并集
print(a - b) # 输出: {1, 2} 差集(在 a 不在 b)
print(a ^ b) # 输出: {1, 2, 5, 6} 对称差(只在一方)
快判断成员 / 去重
names = ["小明", "小红", "小明"]
print(list(set(names))) # 输出: ['小明', '小红'](顺序不定)
# 集合的 in 判断是 O(1),比列表快很多(如 set(range(1000000)) 查成员几乎瞬间)
# ⚠️ 空集合只能写 set(),{} 是空字典
print(type({}), type(set())) # 输出: <class 'dict'> <class 'set'>
四种容器怎么选
| 容器 | 有序 | 可变 | 重复元素 | 典型用途 |
|---|---|---|---|---|
list |
是 | 是 | 允许 | 一串有序数据 |
tuple |
是 | 否 | 允许 | 不该变的记录、字典键 |
dict |
是(3.7+) | 是 | 键唯一 | 键值映射、快速查找 |
set |
否 | 是 | 不允许 | 去重、集合运算、成员判断 |
shopping = ["牛奶", "面包"] # 要顺序、要能改 → list
rgb = (255, 128, 0) # 固定的一组值 → tuple
phone = {"小明": "13800000000"} # 按名字查东西 → dict
tags = {"python", "教程"} # 去重或判断"在不在" → set
嵌套数据结构初识
容器的元素还可以是容器,形成"结构":
students = [
{"name": "小明", "scores": [90, 85, 88]},
{"name": "小红", "scores": [95, 92, 99]},
]
for student in students:
avg = sum(student["scores"]) / len(student["scores"])
print(f"{student['name']} 平均分 {avg:.1f}")
# 输出: 小明 平均分 87.7 / 小红 平均分 95.3
这就是 JSON、配置文件的常见形态,后面 16-数据处理 会大量用到。
常见坑
坑一:赋值不是复制
a = [1, 2, 3]
b = a # b 和 a 指向同一个列表!
b.append(4)
print(a) # 输出: [1, 2, 3, 4] a 也被改了
# ✅ 想要副本用 copy 或切片
b = a.copy() # 或 b = a[:]
b.append(5)
print(a) # 输出: [1, 2, 3, 4]
坑二:sort 返回 None
# ❌ sort 原地排序,返回值是 None
# result = nums.sort() # result 是 None
# ✅ 要么原地排序不看返回值,要么用 sorted 拿新列表
nums = [3, 1, 2]
nums.sort()
result = sorted(nums)
坑三:用可变对象做字典键
# ❌ 列表不可哈希,不能当键:TypeError: unhashable type: 'list'
# d = {[1, 2]: "x"}
# ✅ 用元组
d = {(1, 2): "x"}
print(d[(1, 2)]) # 输出: x
坑四:误以为集合有序
s = {"b", "a", "c"}
print(s) # 输出顺序不确定,别依赖
print(sorted(s)) # 输出: ['a', 'b', 'c'] 需要有序就排序
小结
list有序可变,append/insert/remove/pop是高频操作。sort原地排序返回None;sorted返回新列表。别记反了。tuple不可变,解包和"当字典键"是它的招牌。dict存键值对,取不存在的键用get更安全;items()遍历最常用。set无序不重复,擅长去重和交并差;空集合写set()。- 选择容器看三点:要不要顺序、要不要能改、要不要去重。
b = a只是起别名,要独立副本请用.copy()或切片。- 嵌套容器(列表套字典等)是真实数据的常见形态。
延伸阅读
- 21-推导式与函数式工具:用一行搞定列表生成与筛选
- 16-数据处理:json / csv / dataclasses:容器在真实数据里的用法
上一篇:字符串与格式化 · 下一篇:你好世界与第一个程序
文章回复
0 条公开回复