小票要行号,价目表要把商品和价格配成对。两个都是循环,里面都藏着一个计数器。哪个内置函数能把这份记账活接过去?
两个小工具能省掉大量循环里的杂活。enumerate 会给每个元素一个位置,zip 则让两个或多个序列并排前进。它们返回的都是迭代器,想直接查看结果时记得用 list() 包起来。 你还会发现 zip 的静默截断、用 enumerate 遍历惰性迭代器,并用 zip(*rows) 转置行。
不用手写计数器
enumerate(colors) 每轮产生一对值:索引和元素。直接在 for 语句里解包这一对即可。它比单独维护一个计数器再手动加一更清楚。
colors = ["red", "green", "blue"]
for index, color in enumerate(colors):
print(index, color)索引和元素一起拿到
从 1 开始编号
start 参数只改变第一个编号,不会改变原序列。面向人的步骤或排行榜可以从 start=1 开始。原列表本身不会被修改。
for number, color in enumerate(["red", "green"], start=1):
print(f"{number}: {color}")更适合展示的编号
并行遍历两个列表
zip 会在最短的输入结束时停止。数据本来就该一一对应时这很方便,但长度不一致时也可能掩盖缺失值。需要计数或把配对当成元组打印时,可以先转成列表。
names = ["Ada", "Grace"]
scores = [92, 88]
for name, score in zip(names, scores):
print(f"{name}: {score}")
print(list(zip([1, 2], ["a", "b"])))并行序列
zip 会静默停止,strict 会报错
zip 在最短输入处停止。数据本来一一对应时很方便,缺失一条记录时却会悄悄丢数据。strict=True 会把长度不一致变成 ValueError,而不是截断。在两边本应等长的边界上使用它。
names = ["Ada", "Grace", "Lin"]
scores = [92, 88]
print(list(zip(names, scores)))
try:
print(list(zip(names, scores, strict=True)))
except ValueError as error:
print(type(error).__name__, error)静默截断与明确失败
enumerate 适用于任何迭代器
enumerate 不需要 len() 或下标。它取一个元素,产出一对值,因此生成器、文件和数据库游标都能用。需要位置时用它;不要为了计数先把大序列全部装进列表。
def readings():
yield 4.5
yield 3.0
yield 5.25
for position, value in enumerate(readings(), start=1):
print(position, value)给惰性流编号
用 zip(*rows) 转置行
zip(*rows) 把一组行变成列。它是把名称和价格分开的紧凑写法,但每一行的宽度必须相同。结果是由元组组成的迭代器;需要查看完整形状时再转换。
rows = [("latte", 4.5), ("tea", 3.0), ("mocha", 5.0)]
names, prices = zip(*rows)
print(names)
print(prices)
print(sum(prices))行进去,列出来
本课要点
- enumerate 产生 (索引, 元素) 对。
- 编号要展示给人看时,用 start=1。
- zip 并行遍历序列,并在最短的序列处停止。
- 这两个函数返回迭代器,不是列表。
- zip 在最短输入处静默停止;strict=True 让长度不一致显式失败。
常见错误
- 不转成列表就直接对 enumerate 的结果做下标访问。
- 以为 zip 会用 None 补齐较短的输入;它其实会停止。
- enumerate 已经能表达意图,却仍然手写计数器。
- 假设输入等长,结果丢掉了较长序列的尾部。
给 ['Ada', 'Grace'] 打印从 1 开始的编号,再打印 list(zip([1, 2], ['a', 'b']))。
动手试试
改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。