Python中zip()函数并行遍历多个序列的示例详解
一、开篇:当三个列表需要同步处理时
在实际开发中,我们经常遇到这样的场景:用户姓名在一个列表,年龄在另一个列表,邮箱在第三个列表——它们通过相同的索引对应同一个人。
不用zip的写法:
names = ["张三", "李四", "王五"]
ages = [25, 30, 28]
emails = ["zs@test.com", "ls@test.com", "ww@test.com"]
# ❌ 老式写法
for i in range(len(names)):
print(f"{names[i]}, {ages[i]}岁, {emails[i]}")
用zip的优雅写法:
# ✅ Pythonic!
for name, age, email in zip(names, ages, emails):
print(f"{name}, {age}岁, {email}")
zip()就像一个"拉链",把多个序列的对应位置"拉"在一起。今天我们就来全面掌握zip()的用法、细节和高级技巧。
二、zip()的基本用法
2.1 核心功能
# zip(*iterables) —— 将多个可迭代对象的对应元素打包成元组
numbers = [1, 2, 3]
letters = ['a', 'b', 'c']
symbols = ['!', '@', '#']
# 基本使用
zipped = zip(numbers, letters, symbols)
print(list(zipped)) # [(1, 'a', '!'), (2, 'b', '@'), (3, 'c', '#')]
# 在for循环中直接解包
for num, letter, sym in zip(numbers, letters, symbols):
print(f"{num}{letter}{sym}", end=' ')
# 输出: 1a! 2b@ 3c#
2.2 长度不一致时的行为
# zip() 按最短的可迭代对象截断
short = [1, 2]
long_list = [1, 2, 3, 4, 5]
print(list(zip(short, long_list)))
# [(1, 1), (2, 2)] —— 只有2对!
# ⚠️ 多余的元素被默默丢弃了!
# 如果你不能接受数据丢失,需要使用 itertools.zip_longest
from itertools import zip_longest
print(list(zip_longest(short, long_list)))
# [(1, 1), (2, 2), (None, 3), (None, 4), (None, 5)]
# 指定填充值
print(list(zip_longest(short, long_list, fillvalue=0)))
# [(1, 1), (2, 2), (0, 3), (0, 4), (0, 5)]
# zip函数的巧妙技巧:检查序列长度
# 如果想要在长度不一致时报错
def strict_zip(*iterables):
"""严格的zip——长度不一致时抛出异常"""
sentinel = object()
for i, items in enumerate(zip_longest(*iterables, fillvalue=sentinel)):
if any(item is sentinel for item in items):
raise ValueError(f"可迭代对象长度不一致!在第{i}个位置出现缺失")
yield items
# Python 3.10+ 可以直接用 strict 参数
# list(zip(short, long_list, strict=True)) # ValueError!
2.3 zip对象是迭代器
# zip返回的是一个迭代器,只能遍历一次
z = zip([1, 2, 3], ['a', 'b', 'c'])
print("第一次遍历:")
for item in z:
print(f" {item}")
print("第二次遍历:")
for item in z:
print(f" {item}") # 什么都不输出!迭代器已耗尽
# ✅ 需要多次使用时,转为列表
z_list = list(zip([1, 2, 3], ['a', 'b', 'c']))
for item in z_list:
print(item)
for item in z_list: # 可以再次使用
print(item)
三、zip()的经典应用
3.1 并行遍历
这是zip最常见的用法:
# 多列数据同步处理
students = ["张三", "李四", "王五", "赵六"]
math_scores = [85, 92, 78, 90]
english_scores = [88, 86, 95, 72]
physics_scores = [90, 78, 85, 88]
print(f"{'姓名':<8} {'数学':<6} {'英语':<6} {'物理':<6} {'平均':<6}")
print("-" * 40)
for name, math, eng, phy in zip(students, math_scores, english_scores, physics_scores):
avg = (math + eng + phy) / 3
print(f"{name:<8} {math:<6} {eng:<6} {phy:<6} {avg:<6.1f}")
3.2 构建字典
# zip + dict = 快速创建字典
keys = ["name", "age", "city", "email"]
values = ["张三", 25, "北京", "zs@test.com"]
user = dict(zip(keys, values))
print(user)
# {'name': '张三', 'age': 25, 'city': '北京', 'email': 'zs@test.com'}
# 从两个列表创建映射表
countries = ["中国", "美国", "日本", "英国"]
capitals = ["北京", "华盛顿", "东京", "伦敦"]
country_to_capital = dict(zip(countries, capitals))
print(country_to_capital)
# 从CSV数据一次性构建
headers = ["id", "product", "price", "stock"]
row = [101, "机械键盘", 299.00, 50]
product = dict(zip(headers, row))
print(product)
3.3 转置矩阵
# zip的经典用途:矩阵转置
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
# 转置 = zip(*matrix)
transposed = list(zip(*matrix))
print("转置前:")
for row in matrix:
print(row)
print("转置后:")
for row in transposed:
print(row)
# 输出:
# 转置前: [1,2,3] [4,5,6] [7,8,9]
# 转置后: (1,4,7) (2,5,8) (3,6,9)
# * 解包运算符将matrix的每一行作为zip的单独参数
# zip([1,2,3], [4,5,6], [7,8,9])
# → (1,4,7), (2,5,8), (3,6,9)
# 实际应用:旋转表格数据
def rotate_table(table):
"""旋转表格(行列互换)"""
return [list(row) for row in zip(*table)]
data = [
["姓名", "张三", "李四", "王五"],
["数学", 85, 92, 78],
["英语", 88, 86, 95],
]
rotated = rotate_table(data)
for row in rotated:
print(row)
3.4 成对处理
# 处理序列中的相邻元素对
data = [3, 1, 4, 1, 5, 9, 2, 6]
# zip(data, data[1:]) —— 元素与其下一个元素成对
for prev, curr in zip(data, data[1:]):
change = "↑" if curr > prev else "↓" if curr < prev else "→"
print(f"{prev} → {curr} {change}")
# 计算相邻移动平均
moving_avg = [(a + b) / 2 for a, b in zip(data, data[1:])]
print(f"移动平均: {moving_avg}")
# 比较两个相邻版本的差异
old_version = list(range(10))
new_version = [0, 1, 2, 9, 4, 5, 6, 7, 8, 3] # 3和9互换了
changes = [
(i, old, new)
for i, (old, new) in enumerate(zip(old_version, new_version))
if old != new
]
for idx, old, new in changes:
print(f"位置{idx}: {old} → {new}")
四、zip()与*解包的组合
4.1 zip和unzip
# zip() 和 zip(*zipped) 是一对可逆操作
pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
# unzip:把配对的数据分离回原始序列
numbers, letters = zip(*pairs)
print(numbers) # (1, 2, 3)
print(letters) # ('a', 'b', 'c')
# 完整示例
users = [
("张三", 25, "北京"),
("李四", 30, "上海"),
("王五", 28, "广州"),
]
names, ages, cities = zip(*users)
print(f"姓名: {names}")
print(f"年龄: {ages}")
print(f"城市: {cities}")
# 计算统计信息
print(f"平均年龄: {sum(ages) / len(ages):.1f}")
4.2 数据行与列的转换
# 从行式数据转到列式数据,反之亦然
# 行式:每条记录是一个元组/列表
# 列式:每列是一个列表
rows = [
("张三", 85, 92, 78),
("李四", 90, 88, 95),
("王五", 76, 85, 82),
]
# 行 → 列
columns = list(zip(*rows))
print("列式数据:")
print(f"姓名列: {columns[0]}")
print(f"数学列: {columns[1]}")
print(f"英语列: {columns[2]}")
print(f"物理列: {columns[3]}")
# 列 → 行
rows_again = list(zip(*columns))
print("\n还原为行式:")
for row in rows_again:
print(row)
五、zip()的高级用法
5.1 创建迭代器对比
# 使用zip创建"滑动窗口"
def sliding_window(iterable, n):
"""生成滑动窗口"""
iterators = zip(*(iterable[i:] for i in range(n)))
return iterators
data = list(range(10))
windows = list(sliding_window(data, 3))
print("滑动窗口(大小=3):")
for w in windows:
print(f" {w}")
# 等价于:
# zip(data[0:], data[1:], data[2:])
# itertools中有更高效的实现
# from itertools import pairwise # Python 3.10+
5.2 构建多键排序
# zip用于多键排序(按多个条件排序)
students = [
("张三", 85, 18),
("李四", 85, 17),
("王五", 90, 19),
("赵六", 85, 17),
("钱七", 78, 20),
]
# 要求:先按成绩降序,再按年龄升序,最后按姓名升序
# 技巧:把排序键打包进zip
# 方法1:使用key函数
sorted_students = sorted(
students,
key=lambda s: (-s[1], s[2], s[0])
)
print("排序后:")
for name, score, age in sorted_students:
print(f" {name}: 成绩{score}, 年龄{age}")
# 方法2:使用zip(装饰-排序-去装饰模式)
def sort_by_multiple_keys(data):
# 装饰:创建排序用的键
decorated = [(-s[1], s[2], s[0], s) for s in data]
# 排序
decorated.sort()
# 去装饰
return [item[-1] for item in decorated]
5.3 同时遍历多个字典
# zip遍历多个字典的对应键值
dict1 = {"a": 1, "b": 2, "c": 3}
dict2 = {"a": 10, "b": 20, "c": 30}
dict3 = {"a": 100, "b": 200, "c": 300}
# 确保按键的顺序一致
keys = dict1.keys()
for k, v1, v2, v3 in zip(keys, dict1.values(), dict2.values(), dict3.values()):
total = v1 + v2 + v3
print(f"{k}: {v1} + {v2} + {v3} = {total}")
# 更安全的方式:使用字典推导
# 确保三个字典的键相同
for k in dict1:
if k in dict2 and k in dict3:
print(f"{k}: {dict1[k]}, {dict2[k]}, {dict3[k]}")
六、实战案例
6.1 成绩合并计算器
class ScoreMerger:
"""多科目成绩合并计算 —— zip的综合应用"""
def __init__(self):
self.students = []
def load_data(self, names, *score_lists):
"""加载数据:姓名列表 + 各科成绩列表"""
self.names = names
self.subjects = [f"科目{i+1}" for i in range(len(score_lists))]
self.score_matrix = list(zip(*score_lists)) # 每个学生的各科成绩
def get_student_totals(self):
"""计算每个学生的总分"""
totals = [sum(scores) for scores in self.score_matrix]
return list(zip(self.names, totals)) # 配对上姓名
def get_subject_averages(self):
"""计算每个科目的平均分"""
score_columns = list(zip(*self.score_matrix)) # 转置:每列=每科
avgs = [sum(col) / len(col) for col in score_columns]
return list(zip(self.subjects, avgs))
def get_rankings(self):
"""获取排名"""
totals = self.get_student_totals()
# 按总分降序
totals.sort(key=lambda x: x[1], reverse=True)
return [(rank, name, score)
for rank, (name, score) in enumerate(totals, start=1)]
def print_report(self):
"""打印成绩报告"""
print("=" * 50)
print("📊 成绩总览")
print("=" * 50)
# 表头
header = ["姓名"] + self.subjects + ["总分"]
print(" ".join(f"{h:<8}" for h in header))
print("-" * (9 * len(header)))
# 数据行
for name, scores in zip(self.names, self.score_matrix):
total = sum(scores)
row = [name] + list(scores) + [total]
print(" ".join(f"{str(v):<8}" for v in row))
# 排名
print(f"\n🏆 排名:")
for rank, name, score in self.get_rankings():
medal = {1: "🥇", 2: "🥈", 3: "🥉"}.get(rank, f" {rank}.")
print(f" {medal} {name}: {score}分")
# 科目平均
print(f"\n📈 科目平均分:")
for subject, avg in self.get_subject_averages():
print(f" {subject}: {avg:.1f}")
# 测试
merger = ScoreMerger()
merger.load_data(
["张三", "李四", "王五", "赵六", "钱七"],
[85, 92, 78, 90, 88], # 科目1
[88, 86, 95, 72, 91], # 科目2
[90, 78, 85, 88, 82], # 科目3
)
merger.print_report()
6.2 配置文件对比工具
def compare_configs(config1, config2):
"""对比两个配置字典的差异 —— zip的应用"""
all_keys = sorted(set(config1) | set(config2))
changes = []
for key in all_keys:
v1 = config1.get(key, "<不存在>")
v2 = config2.get(key, "<不存在>")
if v1 != v2:
changes.append({
"key": key,
"old": v1,
"new": v2,
"type": "added" if v1 == "<不存在>"
else "removed" if v2 == "<不存在>"
else "modified"
})
# 打印对比报告
if not changes:
print("✅ 两个配置完全相同")
return
print("=" * 60)
print("📋 配置对比报告")
print("=" * 60)
for i, change in enumerate(changes, 1):
typ = change["type"]
key = change["key"]
if typ == "added":
print(f"{i}. [+] {key}: {change['new']}")
elif typ == "removed":
print(f"{i}. [-] {key}: {change['old']} (已删除)")
else:
print(f"{i}. [~] {key}: {change['old']} → {change['new']}")
# 测试
old_config = {
"host": "localhost",
"port": 8080,
"debug": True,
"timeout": 30,
"retry": 3,
}
new_config = {
"host": "0.0.0.0",
"port": 443,
"debug": False,
"timeout": 30,
"ssl": True,
}
compare_configs(old_config, new_config)
七、本章小结
本文我们全面学习了zip()函数:
基本功能:zip(*iterables)将多个可迭代对象的对应元素打包成元组。按最短序列截断,返回迭代器。
zip_longest:itertools.zip_longest按最长序列处理,缺失位置用fillvalue填充。
经典应用:
- 并行遍历:多列数据同步处理
- 构建字典:
dict(zip(keys, values)) - 矩阵转置:
zip(*matrix) - 成对处理:
zip(data, data[1:])
zip与*解包:zip(*pairs)可以"解压缩",把配对数据分离回原始序列。
Python 3.10+ strict参数:zip(a, b, strict=True)在长度不一致时抛出ValueError。
zip()是Python中"让代码更简洁"的利器。当你发现自己在用range(len())或者一个个地手动配对数据时,想想zip()——它几乎总能提供更优雅的方案。⌨️ 下一篇文章,我们将学习break与continue循环控制。
以上就是Python中zip()函数并行遍历多个序列的示例详解的详细内容,更多关于Python zip并行遍历多个序列的资料请关注脚本之家其它相关文章!
相关文章
PyTorch中dataloader制作自定义数据集的实现示例
本文主要介绍了PyTorch中dataloader制作自定义数据集的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2025-05-05
PyTorch-Forecasting一个新的时间序列预测库使用详解
这篇文章主要为大家介绍了PyTorch-Forecasting一个新的时间序列预测库示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-05-05


最新评论