Python小技巧:用zip()同时遍历多个列表,告别繁琐的索引循环
标签搜索

Python小技巧:用zip()同时遍历多个列表,告别繁琐的索引循环

xiaoxiao
2026-10-19 / 0 评论 / 1 阅读 / 正在检测是否收录...

你是不是也这样写过代码?

刚开始学 Python 的时候,很多同学会遇到这样的场景:手里有几个长度相同的列表,想把它们的元素一一对应地拿出来处理。比如名字和成绩要配对,商品名和价格要配对。

很多人的第一反应是写一个带索引的 for 循环:

names = ["小明", "小红", "小李"]
scores = [92, 88, 95]

for i in range(len(names)):
    print(names[i], scores[i])

这段代码能跑通,但问题不少:

  • 代码可读性差,names[i]、scores[i] 这种写法一眼看不出在干什么;
  • 列表一多,括号套括号,越写越乱;
  • 万一两个列表长度不一致,还容易出现索引越界,或者悄悄漏掉元素。

今天介绍的 zip() 就是来解决这个问题的。学会它,你的循环代码会立刻变得简洁又优雅。

zip() 是什么?

zip() 是 Python 的内置函数,作用是把多个可迭代对象(列表、元组、字符串等)"拉链式"地组合在一起。它会从每个对象中各取一个元素,打包成一个元组,然后返回一个由这些元组成的可迭代对象。

把上面的例子改用 zip() 写:

names = ["小明", "小红", "小李"]
scores = [92, 88, 95]

for name, score in zip(names, scores):
    print(name, score)

输出:

小明 92
小红 88
小李 95

对比一下就知道差别了:

  • 不再需要 range(len(...));
  • 不再需要下标 i;
  • 循环变量 name、score 名字清晰,读代码的人一眼就能明白每个值的含义。

这就是 zip() 最常见的用法:同时遍历多个列表。

看看 zip() 到底打包了什么

如果你想直接看到 zip() 的产出,可以用 list() 把它转换成列表:

letters = ["a", "b", "c"]
numbers = [1, 2, 3]

print(list(zip(letters, numbers)))
# [('a', 1), ('b', 2), ('c', 3)]

每个元素都是一个元组,第一个位置来自第一个列表,第二个位置来自第二个列表,像拉链的齿一样一一对应。

zip() 不限于两个列表,三个、四个甚至更多都可以:

names = ["小明", "小红", "小李"]
scores = [92, 88, 95]
levels = ["A", "B", "A"]

for name, score, level in zip(names, scores, levels):
    print(f"{name}:{score} 分,等级 {level}")

输出:

小明:92 分,等级 A
小红:88 分,等级 B
小李:95 分,等级 A

注意这里用了多个循环变量来接收元组中的每个元素,这种写法叫"解包",是 zip() 的标准搭配。

一个必须知道的细节:长度不等会怎样?

zip() 遍历的长度,取决于最短的那个可迭代对象。一旦最短的列表用完,循环就结束了,多出来的元素会被直接忽略。

names = ["小明", "小红", "小李", "小王"]
scores = [92, 88, 95]

for name, score in zip(names, scores):
    print(name, score)

输出:

小明 92
小红 88
小李 95

"小王"被悄悄丢掉了,程序不会报错。这种静默截断在某些场景是合理的,但如果你希望遍历到最长的那个列表,就需要用 zip_longest()。

进阶用法一:zip_longest() 处理不等长列表

zip_longest 来自标准库 itertools。它会遍历到最长的列表结束为止,缺失的位置用你指定的默认值填充。

from itertools import zip_longest

names = ["小明", "小红", "小李", "小王"]
scores = [92, 88, 95]

for name, score in zip_longest(names, scores, fillvalue="缺考"):
    print(name, score)

输出:

小明 92
小红 88
小李 95
小王 缺考

当你不确定两个列表长度是否一致,又不想漏掉任何元素时,zip_longest() 是更安全的选择。

进阶用法二:用 zip() 轻松构造字典

把"键列表"和"值列表"合成字典,是 zip() 的经典应用:

keys = ["name", "age", "city"]
values = ["小明", 18, "北京"]

info = dict(zip(keys, values))
print(info)
# {'name': '小明', 'age': 18, 'city': '北京'}

一行代码搞定。如果不熟悉 zip(),很多人会写成带索引的循环逐个赋值,既啰嗦又容易写错。

进阶用法三:zip(*) 反向解压

你可能见过 zip(*) 这种写法,它其实是 zip() 的逆操作。先把行打包成列,或者把列还原成行:

rows = [("小明", 92), ("小红", 88), ("小李", 95)]

names, scores = zip(*rows)
print(names)   # ('小明', '小红', '小李')
print(scores)  # (92, 88, 95)

原理是:*rows 会把列表解包成多个独立的参数传给 zip(),于是 zip(("小明", 92), ("小红", 88), ("小李", 95)) 把每个元组的第一个元素聚成一组、第二个元素聚成一组。

这个技巧在处理二维数据、批量拆分字段时非常好用。

进阶用法四:同时遍历并带上下标

有时候你既想用 zip() 的简洁,又需要知道当前是第几轮循环。enumerate() 可以嵌在 zip() 外面:

names = ["小明", "小红", "小李"]
scores = [92, 88, 95]

for i, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"第{i}名:{name},{score} 分")

输出:

第1名:小明,92 分
第2名:小红,88 分
第3名:小李,95 分

注意 (name, score) 外面的那层括号不能省,它表示先把 zip 的元组解包,再交给 enumerate。

什么时候不该用 zip()?

zip() 虽好,但不是万能的:

  1. 长度必须严格一致的场景:默认的 zip() 会静默截断,如果你需要保证"一个都不能少",要么先检查长度,要么用 zip_longest() 并检查填充值。
  2. 需要随机访问的场景:zip() 返回的是可迭代对象,只适合顺序遍历。如果你经常要取第 N 个元素,直接用列表下标反而更直接。
  3. 只有一个列表时:直接 for x in lst 就够了,不必多此一举。

小结

  • zip() 把多个可迭代对象按位置打包成元组,让"同时遍历多个列表"变得简单;
  • 配合解包语法,循环变量名清晰,可读性远胜索引循环;
  • 注意 zip() 默认以最短的列表为准,不等长时可用 itertools.zip_longest();
  • dict(zip(keys, values)) 可以快速构造字典,zip(*) 可以反向拆分数据。

下次再想写 for i in range(len(...)) 的时候,先停一秒,想想是不是可以用 zip() 更优雅地解决。养成这个习惯,你的 Python 代码会明显上一个台阶。

动手试试看:把你手头某个用索引遍历多个列表的小脚本,改写成 zip() 版本,感受一下代码变短、变清晰的过程吧。

0

评论 (0)

取消