☰
Python函数之zip函数的介绍与实际应用
2026/10/16 7:24:49 网站建设 项目流程

前言


zip()的名字来自「拉链」:它把多个可迭代对象并排咬合在一起,第 i 次产出「每个对象各自的第 i 个元素」组成的元组。它解决的是「同时遍历多个序列」这类需求——不用再手写下标去对齐两个列表。


三个必须先纠正的误解:



  • 以为zip()返回列表。在 Python 3 里它返回的是迭代器,惰性求值、只能消费一次。Python 2 里它确实返回列表,这也是很多老教程会写错的原因(Python 2 已在 2020 年 1 月 1 日停止维护,iteritems、xrange这些写法都已经移除或改名)。

  • 以为两个序列长度不同会报错。默认行为是按最短的那个截断,多余的元素被静默丢弃。想让它报错,得显式传strict=True。

  • 以为zip()会「复制」数据。它一个元素都不复制,只在你迭代时按需向各个可迭代对象取下一个值。


一、签名与语义


官方签名是:


zip(*iterables, strict=False)

文档的描述很精确:zip()返回一个元组的迭代器,第 i 个元组包含每个参数可迭代对象的第 i 个元素。可以把它想成「把行变成列、把列变成行」,也就是矩阵转置。


# 适用于 Python 3.8+
print(list(zip([1, 2, 3], ["a", "b", "c"])))
# [(1, 'a'), (2, 'b'), (3, 'c')]

几个容易被忽略的边界:


# 适用于 Python 3.8+
print(list(zip())) # [],不传参得到空迭代器
print(list(zip([1, 2, 3]))) # [(1,), (2,), (3,)],只传一个时是单元素元组

zip()是惰性的:元素不会在创建时被处理,直到你真正迭代它(for循环或包一层list())。


二、长度不等怎么办


这是zip()最容易出静默 bug 的地方。默认是「按最短截断」:


# 适用于 Python 3.8+
print(list(zip(range(3), ["fee", "fi", "fo", "fum"])))
# [(0, 'fee'), (1, 'fi'), (2, 'fo')]

注意最后那个'fum'被悄悄丢掉了,没有任何提示。如果你确信几个序列应该等长,就应该用strict=True——它需要Python 3.10 及以上(来自 PEP 618):


# 适用于 Python 3.10+
print(list(zip(("a", "b", "c"), (1, 2, 3), strict=True)))
# [('a', 1), ('b', 2), ('c', 3)]

长度不一时它会直接抛ValueError,而不是截断:


# 适用于 Python 3.10+
list(zip(range(3), [1, 2, 3, 4], strict=True))

Traceback (most recent call last):
...
ValueError: zip() argument 2 is longer than argument 1



场景写法行为



明确知道会不等长,短的为准zip(a, b)截断到最短

认为它们应该等长zip(a, b, strict=True)(3.10+)不等长抛ValueError

需要保留最长的zip_longest(itertools)用填充值补齐短的一方



itertools.zip_longest是标准库里补这一块的工具,它接受fillvalue指定填充值。


三、常见用法


用法一:同时遍历多个序列。


# 适用于 Python 3.8+
names = ["ann", "bob", "cid"]
scores = [95, 88, 90]

for name, score in zip(names, scores):
print(f"{name}: {score}")

用法二:用两个序列拼出字典。


# 适用于 Python 3.8+
keys = ["name", "age", "city"]
values = ["ann", 20, "beijing"]
print(dict(zip(keys, values)))
# {'name': 'ann', 'age': 20, 'city': 'beijing'}

用法三:转置矩阵。


# 适用于 Python 3.8+
matrix = [
[1, 2, 3],
[4, 5, 6],
]
print(list(zip(*matrix)))
# [(1, 4), (2, 5), (3, 6)]

这里的*matrix把三行拆成三个参数传给zip,于是得到三列。


用法四:反向操作——把「元组列表」拆成两个序列。


# 适用于 Python 3.8+
pairs = [(1, "a"), (2, "b"), (3, "c")]
nums, letters = zip(*pairs)
print(nums) # (1, 2, 3)
print(letters) # ('a', 'b', 'c')

注意解包出来的是元组,不是列表。


用法五:配合enumerate给结果编号。


# 适用于 Python 3.8+
a = ["x", "y"]
b = [1, 2]
for i, (left, right) in enumerate(zip(a, b), start=1):
print(i, left, right)

四、完整示例:对齐两个数据源


一个常见的真实场景:一份是「列名」,另一份是「每行的值」,要把它们拼成字典列表。


# 适用于 Python 3.8+
def rows_to_dicts(columns, rows, strict=True):
"""把列名和每行数据对齐成字典列表。
strict 默认开启,防止列数和值数不一致时被静默截断。"""
result = []
for row in rows:
if len(row) != len(columns):
raise ValueError(f"列数与值数不一致:{len(columns)} vs {len(row)}")
result.append(dict(zip(columns, row, strict=strict)))
return result


if __name__ == "__main__":
columns = ["name", "score"]
rows = [["ann", 95], ["bob", 88]]
for item in rows_to_dicts(columns, rows):
print(item)

{'name': 'ann', 'score': 95}
{'name': 'bob', 'score': 88}

这里strict=True是双保险:函数里已经用len检查过一次,zip的strict负责在真正拼接时再兜一次底。


五、zip 与其它工具的关系




工具产出长度不等时备注



zip(a, b)迭代器,产出元组截断到最短Python 3 起是迭代器

zip(a, b, strict=True)同上抛ValueError需 3.10+

itertools.zip_longest(a, b)同上用fillvalue补齐结果最长

enumerate(x)迭代器,产出(序号, 元素)不涉及只有一个序列

map(f, a, b)迭代器截断到最短直接对每对元素调用f



map其实也能接受多个可迭代对象,效果类似zip加处理函数,它同样在最短处停止。


常见坑点



  1. 把 zip 对象当成可重复遍历的容器


❌z = zip(a, b),第一次for用完,第二次for什么都不输出 ✅ 迭代器一次性;要多次使用就重新zip(a, b)或先list()



  1. 以为长度不等会报错


❌for k, v in zip(keys, values),少了几个值却毫无提示 ✅ 确信等长就加strict=True(3.10+),或先自查长度



  1. 在 zip 里传进「一次性」的迭代器后重复使用


❌ 传的是生成器,zip消费后再用同一个生成器得到空 ✅ 生成器同样只能消费一次,需要复用就先物化成list



  1. 以为zip(*matrix)返回列表


❌zip(*matrix).append(...),AttributeError✅ 它是迭代器,要list(zip(*matrix))才能当下标访问



  1. 解包后忘了得到的是元组


❌nums, letters = zip(*pairs)之后对nums调append✅ 解包结果是元组,需要列表就再list(...)



  1. 误用 Python 2 的预期


❌ 照抄老代码里「zip返回列表、可以len(zip(...))」的用法 ✅ Python 3 里zip返回迭代器,没有len;要长度先转list



  1. strict参数用在不支持的版本


❌ 在 3.9 及以前写zip(a, b, strict=True)✅strict是3.10 新增(PEP 618);旧版本自行比较长度



  1. 用 zip 传单个长序列却忘记它是单元素元组


❌for x in zip(data): ...之后把x当元素用 ✅ 单参数zip产出的是(元素,)这样的一元组


总结




关注点结论



签名zip(*iterables, strict=False)

返回类型迭代器,产出元组

惰性是,迭代时才取值

可变现次数一次

长度不等默认行为截断到最短

strict=True3.10+,不等长抛ValueError

典型搭档dict()、enumerate、解包、itertools.zip_longest



zip的用法可以用一句话概括:它负责把多个序列按位置对齐。用的时候盯紧三件事——它是迭代器、它只能消费一次、它默认按最短截断且不报警。第三点尤其值得警惕,因为被丢掉的元素不会以任何形式提醒你。




需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询