共计 3434 个字符,预计需要花费 9 分钟才能阅读完成。
导读 | Python 中有一些内置函数,可以使我们的代码非常优雅。zip 函数就是其中之一,但是 zip 函数的使用对于初学者来说不是很直观,有时容易出错。因此本文将从 7 个层次来由浅入深地来探讨强大的zip 函数的概念、用法和技巧。 |
zip 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的iterator。
举例如下,我们可以使用它以如下方式来组合两个列表,样例代码如下:
id = [1, 2, 3, 4] | |
leaders = ['Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li'] | |
record = zip(id, leaders) | |
print(record) | |
# [(1, 'Elon Mask'), (2, 'Tim Cook'), (3, 'Bill Gates'), (4, 'Bai Li')] |
如上面的示例所示,zip 函数返回一个元组迭代器,其中第 i 个元组包含每个列表中的第i 个元素。
事实上,Python 中的zip 函数具有强大的功能,比如它可以一次处理任意数量的可迭代项,而不仅仅是两个。
首先,我们来看如果我们将一个 list 传递给 zip 函数,样例如下:
id = [1, 2, 3, 4] | |
record = zip(id) | |
print(list(record)) | |
# [(1,), (2,), (3,), (4,)] |
如果我们同时传递三个list ,结果如下:
id = [1, 2, 3, 4] | |
leaders = ['Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li'] | |
sex = ['m', 'm', 'm', 'm'] | |
record = zip(id, leaders, sex) | |
print(list(record)) | |
# [(1, 'Elon Mask', 'm'), (2, 'Tim Cook', 'm'), (3, 'Bill Gates', 'm'), (4, 'Bai Li', 'm')] |
如上所述,无论我们传递给zip函数多少个可迭代项,它都能按照我们的预期来正常工作。
顺便说一下,如果没有参数,zip 函数只返回一个空的迭代器。
真实数据并不总是干净和完整的,有时我们必须处理不等长的可迭代数据。默认情况下,zip函数的结果基于最短的可迭代项。
举例如下:
id = [1, 2] | |
leaders = ['Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li'] | |
record = zip(id, leaders) | |
print(list(record)) | |
# [(1, 'Elon Mask'), (2, 'Tim Cook')] |
如上面的代码所示,最短的列表是id ,因此record 只包含两个元组,并且忽略了列表leaders 中的最后两个元素。
如果最后两位leader 因被忽视而不高兴,我们该怎么办?
Python 将再次帮助我们。itertools 模块中还有一个名为zip_langest 的函数。顾名思义,它是zip函数的兄弟,其结果基于最长的参数。
我们不妨使用zip_langest 函数来生成上述record列表,结果如下:
from itertools import zip_longest | |
id = [1, 2] | |
leaders = ['Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li'] | |
long_record = zip_longest(id, leaders) | |
print(list(long_record)) | |
# [(1, 'Elon Mask'), (2, 'Tim Cook'), (None, 'Bill Gates'), (None, 'Bai Li')] | |
long_record_2 = zip_longest(id, leaders, fillvalue='Top') | |
print(list(long_record_2)) | |
# [(1, 'Elon Mask'), (2, 'Tim Cook'), ('Top', 'Bill Gates'), ('Top', 'Bai Li')] |
如上所述,zip_langest 函数基于其最长参数来返回结果。可选的fillvalue 参数(默认值为None)可以帮助我们填充缺失的值。
在上一个示例中,如果我们首先获得列表record,那么我们如何将其unzip 解压缩为单独的可迭代项?
不幸的是,Python 并没有直接的解压缩unzip 函数。然而,如果我们熟悉星号* 的技巧,解压缩将是一项非常简单的任务。
record = [(1, 'Elon Mask'), (2, 'Tim Cook'), (3, 'Bill Gates'), (4, 'Bai Li')] | |
id, leaders = zip(*record) | |
print(id) | |
# (1, 2, 3, 4) | |
print(leaders) | |
# ('Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li') |
在上面的示例中,星号执行了拆包操作,即从记录列表中拆包所有四个元组。
受益于功能强大的zip 函数,基于一些独立的列表来创建和更新 dict 将非常方便。
我们可以使用以下one-line的方案:
● 使用字典生成式和 zip 函数
● 使用 dict 和 zip 函数
样例代码如下:
id = [1, 2, 3, 4] | |
leaders = ['Elon Mask', 'Tim Cook', 'Bill Gates', 'Bai Li'] | |
# create dict by dict comprehension | |
leader_dict = {i: name for i, name in zip(id, leaders)} | |
print(leader_dict) | |
# {1: 'Elon Mask', 2: 'Tim Cook', 3: 'Bill Gates', 4:'Bai Li'} | |
# create dict by dict function | |
leader_dict_2 = dict(zip(id, leaders)) | |
print(leader_dict_2) | |
# {1: 'Elon Mask', 2: 'Tim Cook', 3: 'Bill Gates', 4: 'Bai Li'} | |
# update | |
other_id = [5, 6] | |
other_leaders = ['Larry Page', 'Sergey Brin'] | |
leader_dict.update(zip(other_id, other_leaders)) | |
print(leader_dict) | |
# {1: 'Elon Mask', 2: 'Tim Cook', 3: 'Bill Gates', 4: ''Bai Li'', 5: 'Larry Page', 6: 'Sergey Brin'} |
上面的示例根本不使用for 循环 , 这是多么的优雅和Pythonic!
同时处理多个可迭代项通常是常见的场景,此时我们可以在for循环中配合使用函数zip,这也是我最喜欢函数zip 的用法之一。
举例如下:
products = ["cherry", "strawberry", "banana"] | |
price = [2.5, 3, 5] | |
cost = [1, 1.5, 2] | |
for prod, p, c in zip(products, price, cost): | |
print(f'The profit of a box of {prod} is £{p-c}!') | |
# The profit of a box of cherry is £1.5! | |
# The profit of a box of strawberry is £1.5! | |
# The profit of a box of banana is £3! |
我们来看以下问题:
如何优雅地实现矩阵的转置操作?
Wow, 鉴于在上文中我们已经介绍了函数zip,星号* , 以及列表生成式,所以one-line 的实现方式如下:
matrix = [[1, 2, 3], [1, 2, 3]] | |
matrix_T = [list(i) for i in zip(*matrix)] | |
print(matrix_T) | |
# [[1, 1], [2, 2], [3, 3]] |
