Python zip() 语法
zip() 是 Python 的一个内置函数,它接收多个可迭代对象(如列表)作为参数,然后将这些对象相同位置的元素打包成一个个元组,最后返回由这些元组组成的 zip 对象。
语法:
zip(iterable1, iterable2, ..., iterableN, strict=False)说明:
zip() 函数接收以下参数。
iterable1, iterable2, ..., iterableN(必选):都是可迭代对象。strict(可选,默认值:False):是一个布尔值。如果为 True,当可迭代对象长度不一致时抛出 ValueError。
如果只有一个可迭代对象作为参数传入 zip(),它会返回一个由 “单个元素元组” 组成的迭代器。如果没有传入任何可迭代对象,zip() 将返回一个空的迭代器。
注意:
- zip() 会在最短的可迭代对象用尽时停止生成元组。这意味着如果传入的可迭代对象长度不一,zip() 不会报错,但会 “截断” 到最短的那个。
- zip() 返回的是一个迭代器,而不是一个列表或元组。zip() 采用的是惰性求值,在需要时才生成元组,这对于处理大型数据集非常高效。
Python zip() 摘要
| 使用频率 | 高 |
|---|---|
| 时间复杂度 | O(1) |
| 官方文档 | 查看 |
| 相关函数 | map()、enumerate() |
Python zip() 示例
接下来,我们通过几个简单的例子来讲解一下 Python zip() 函数是如何使用的。
示例 1:zip() 接收 2 个可迭代对象
names = ['Jack', 'Lucy', 'Tony']
ids = [1001, 1002, 1003]
result = zip(names, ids)
print(result)
print(list(result))运行结果如下。
<zip object at 0x000002158749D340>
[('Jack', 1001), ('Lucy', 1002), ('Tony', 1003)]分析:
zip() 返回的是一个 zip 对象,这里使用了 list() 函数将其转化为列表。当然了,你也可以使用 tuple()、str()、dict()、set() 等函数转换成你想要的类型。
示例 2:zip() 接收 n 个可迭代对象
names = ['Jack', 'Lucy', 'Tony']
ids = [1001, 1002, 1003]
grades = [95, 100, 85]
result = zip(names, ids, grades)
print(result)
print(list(result))运行结果如下。
<zip object at 0x00000250E8BFD280>
[('Jack', 1001, 95), ('Lucy', 1002, 100), ('Tony', 1003, 85)]分析:
不管 zip() 接收多少个可迭代对象作为参数,它都是将相同位置的元素组成一个个元组。
示例 3:使用 “* 运算符” 解包
students = [('Jack', 20), ('Lucy', 21), ('Tony', 19)]
names, ages = zip(*students)
print(names)
print(ages)运行结果如下。
('Jack', 'Lucy', 'Tony')
(20, 21, 19)分析:
*students 表示对 students 进行解包,此时 zip(*students) 得到的是:
zip(('Jack', 20), ('Lucy', 21), ('Tony', 19))由于 zip() 函数是将相同位置的元素打包在一起,此时迭代器内部会生成两个元组:('Jack', 'Lucy', 'Tony') 和 (20, 21, 19)。通过多变量赋值机制,这两个元组被分别解包并赋给了 names 和 ages 变量。
示例 4:zip() 使用 strict 模式
try:
list(zip([1, 2], ['a'], strict=True))
except ValueError as e:
print(e)运行结果如下。
zip() argument 2 is shorter than argument 1分析:
当 strict = True 时,如果长度不一致,则会抛出 ValueError 异常。
深入了解 zip()
zip() 函数不仅仅是简单地打包数据,它还有一些非常重要的特性和高级用法,能让我们的代码更简洁高效。
1. 惰性计算特性
zip 对象是一个迭代器,具有惰性计算的特性。这意味着它不会立即生成所有结果,而是在需要时才计算。
比如在下面代码中,由于迭代器只能遍历一次,因此第一次调用 list(z) 时会输出正确结果。但第二次调用时,迭代器已经耗尽,此时返回的是一个空列表。如果需要重复使用,我们可以将其转换为列表或重新创建 zip 对象。
z = zip([1, 2], ['a', 'b'])
print(list(z)) # 输出 : [(1, 'a'), (2, 'b')]
print(list(z)) # 输出 : [] (迭代器已耗尽)2. 与 itertools.zip_longest() 的区别
zip() 函数在处理长度不一致的可迭代对象时,会以最短的那个可迭代对象为准,当最短的序列用尽时就停止。这可能会导致数据截断。
如果我们希望结果的长度与最长的可迭代对象保持一致,并且用一个默认值填充那些不足的空缺,此时需要使用 itertools 模块中的 zip_longest() 函数。
比如在下面代码中,list(zip_longest([1, 2, 3], ['a', 'b'], fillvalue='-')) 会将第三个位置填充为 '-',确保结果长度与最长列表一致。
from itertools import zip_longest
list1 = [1, 2, 3]
list2 = ['a', 'b']
print(list(zip_longest(list1, list2, fillvalue='-'))) # 输出 : [(1, 'a'), (2, 'b'), (3, '-')]3. 转置二维矩阵
zip() 函数还有一个非常巧妙且强大的用途:转置二维矩阵(即交换矩阵的行和列)。这通常通过结合星号操作符 (*) 来实现。
“*” 在这里起到了 “解包 (unpacking)” 的作用。当我们将一个可迭代对象(比如一个列表的列表,它代表矩阵的行)前面加上 “*” 并传递给 zip() 时, “*” 会把这个可迭代对象中的每个子元素(即矩阵的每一行)解包成独立的参数。
然后,zip() 就会将这些独立的 “行” 视为单独的可迭代对象,并像平常一样将它们对应位置的元素打包成元组,从而实现了行列的互换。
比如在下面代码中,list(zip(*matrix)) 将 [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 转置为 [(1, 4, 7), (2, 5, 8), (3, 6, 9)],从而实现行列互换的效果。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = list(zip(*matrix))
print(transposed) # 输出 : [(1, 4, 7), (2, 5, 8), (3, 6, 9)]