变量只能装一个值,真正写程序得把一堆值装进盒子。
list / str / tuple / set / dict 五种容器,先给一张「怎么选」的表,再给 9 段能直接复制运行的代码,最后把它们拼成一个完整的小程序。
9 段代码在 Python 3.11 下跑通,每段都能单独运行,不依赖上下文。
{} 看着像空集合,其实是空字典;想建空集合,只能写 set()。
这类「看着差不多、用起来完全不一样」的地方,数据容器里特别多。
先记三问:这份数据要不要改、要不要去重、是不是按名字取值。三问下来,用哪个容器就定了。
一、30 秒选容器
一句话记:要改选 list,不改选 tuple,去重选 set,按名字存取选 dict。
二、list:要改,就选它
能存不同类型的元素,能增、能删、能改,是默认的万能选手。
s = [56, 12, 90]
s.append(7)
s.insert(1, 30)
s.pop()
print(s) # [56, 30, 12, 90]
for item in s:
print(item, end=" ")
切片是序列类型的通用技能,str 和 tuple 也能用:
s = [1, 2, 3, 4, 5]
print(s[0:3]) # [1, 2, 3],含头不含尾
print(s[3:0:-1]) # [4, 3, 2],步长为负就从右往左
print(s[::-1]) # 整个反转
解包用 * 把列表拆成一个个元素;推导式则是一行生成新列表,比写循环短得多:
a = [10, 25, 89, 12, 88]
b = [11, 29, 89, 12, 90]
merged = [*a, *b] # 解包合并
unique = []
for n in merged:
if n not in unique:
unique.append(n)
print(unique) # 去重,且保留原顺序
print([i ** 2 for i in range(1, 6)]) # [1, 4, 9, 16, 25]
print([i for i in a if i > 50]) # [89, 88]
三、str 和 tuple:改不了的两种
字符串和元组都是不可变的:没有 append,也不能 s[0] = "H",所有「修改」其实都是生成一个新对象。
s = "Hello Python"
print(s[6:]) # Python
print(s[::-1]) # nohtyP olleH
print(s.find("Python")) # 6,找不到返回 -1
print(s.upper(), s.lower())
print(s.replace("o", "A"))
print(s.split(" ")) # ['Hello', 'Python']
元组常用来放「一组不该被改的记录」,只有 count 和 index 两个方法。它真正好用的地方是解包:
t1 = (11, 24, 53, 68, 90, 24, 90)
print(t1.count(90), t1.index(90)) # 2 4
print(type((100)), type((100, ))) # 单元素元组,逗号不能省
a, b, *rest = t1 # * 收集剩下的元素,得到列表
print(a, b, rest)
x, y = 10, 20
x, y = y, x # 交换两个变量,本质是元组解包
print(x, y)
四、set:去重和交并差
无序、不重复,最擅长「这堆东西里有哪些、两边共有哪些」。
s1 = {"A", "C", "E", "H"}
s2 = {"A", "B", "C", "D"}
print(s1 - s2) # 差集:只在 s1 里
print(s1 | s2) # 并集:全部
print(s1 & s2) # 交集:两边都有
print(sorted(set([1, 2, 2, 3, 3]))) # 去重
empty = set() # 空集合只能这么建,{} 是空字典
五、dict:按名字取值
没有下标,靠 key 取值。key 必须是不可变类型(str、int、tuple 都行,list 不行)。
d = {"王林": 670, "李慕婉": 608}
d["韩立"] = 688 # 增 / 改
print(d.get("张三", "查无此人")) # 找不到不报错
print(list(d.keys()), list(d.values()))
for k, v in d.items():
print(k, v)
print(d.pop("王林")) # 删除并拿到值
六、把它们拼起来:一个完整的小程序
一组成绩记录,把 tuple 存记录、dict 按名字索引、list 推导式算分、set 数人头,全串起来:
students = (
("S001", "王林", 85, 92, 78),
("S002", "李慕婉", 92, 88, 95),
("S003", "张三", 78, 85, 82),
)
scores = {}
for sid, name, chinese, math, english in students: # 元组解包
scores[name] = (chinese, math, english) # dict 按名字存
for name, (c, m, e) in scores.items():
print(f"{name} 总分 {c + m + e} 平均 {(c + m + e) / 3:.1f}")
chinese = [c for c, m, e in scores.values()] # 推导式
print(f"语文平均 {sum(chinese) / len(chinese):.1f}")
print(f"共 {len(set(scores))} 人") # set 去重
到这里,你已经能自己决定「这份数据该装进哪个盒子」,并且写出来跑通了。
七、5 个最容易踩的坑
Warning
下面 5 条,跑一遍这段代码就全懂了:
a = [1, 2, 3]
b = a
b.append(4)
print("b = a 之后:", a) # a 也跟着变了
print(type({}), type(set())) # {} 是 dict
print(type((100)), type((100, ))) # 少个逗号,元组就变成 int
- 1.
b = a不是复制,两个名字指向同一个列表。要副本用 a.copy() 或 a[:]。 - 2.
{} 是空字典,不是空集合;空集合必须写 set()。 - 3. 单元素元组要写
(100, ),(100) 只是加了括号的整数。 - 4.
str 和 tuple不可变,s[0] = "H" 会直接 TypeError;s.replace(...) 返回的是新对象,原串没变。 - 5.
dict 的 key 必须不可变,拿 list 或 set 当 key 会报 TypeError;取值用 d.get(k) 比 d[k] 更稳,找不到不会崩。
八、收尾
一句话:容器不是背 API,是先问「这份数据要不要改、要不要去重、要不要按名字取」,答案出来,容器就定了。
这 9 段你都跑通了吗?卡在哪一段,把报错贴到评论区,我看到就回。
觉得有用,顺手点个「在看」,或转给正在入门的朋友。