geekdoc-python-zh/docs/pythoncentral/how-to-use-the-python-zip-f...

12 KiB
Raw Permalink Blame History

如何使用 Python Zip 函数(快速简单)

原文:https://www.pythoncentral.io/how-to-use-the-python-zip-function-fast-and-easy/

Python 的 zip()函数可以帮助开发人员从多种数据结构中对数据进行分组。元素按照它们出现的顺序连接;有了它,您可以同时迭代不同的数据结构。所有这些都没有使用一个嵌套循环!

如果这是你想要实现的目标,你需要学会使用 zip()。该功能最大的优点是使用起来并不复杂。

在本指南中,我们将带您了解如何使用 zip()并并行迭代不同的数据结构。

Python 中的 zip()是什么?

在 Python 中zip()同时合并几个被称为 iterables 的对象。它通过创建一个新对象来实现这一点,该对象的元素都是元组。

可迭代的元素保存在这些元组中。让我们看一个简单的例子来理解 zip()函数。

假设我们有两个包含名字的元组,就像这样:

names1 = ("Jack", "Theo", "Jose")

names2 = ("Duncan", "Chris", "Anthony")

所以,如果要对这些元组的元素进行配对,可以使用 zip()。函数是这样循环的:

print(zip(names1,names2))

当然,打印功能将帮助我们了解使用 zip 时会发生什么。运行这段代码,您会看到它返回一个特定的 zip 对象。代码的输出看起来会像这样:

<zip object at 0x7f7991fc4cc0>

现在,让我们来看看如何将这个 zip 对象转换成一个列表。就写这么简单:

print(list(zip(names1,names2)))

再次运行代码,您将看到输出:

| [( 【杰克】 【邓肯】 )( 【西奥】 【克里斯】 )( 【何塞】【安东尼】)】 |

到目前为止,我们已经介绍了这个函数的基本工作原理。我们将在接下来的章节中讨论 zip 函数的几个细节。

如果你想看看官方对 zip()函数的定义, 这里是官方 Python 文档页面的链接 。请注意,您还可以在文档中找到 zip()函数的源代码,如果您想了解该函数的技术工作方式,这将很有帮助。

对各种数据结构使用 zip()函数

在上一节的示例程序中,我们探讨了如何使用 zip()函数将两个列表合并成一个对象——zip 对象。对象的元素是长度为 2 的元组。

要利用 zip()函数的功能,您必须理解该函数可以处理所有类型的数据结构。在这一节中,我们将探索 zip()是如何做到这一点的。

使用 zip()和元组

向 zip()函数传递两个元组类似于向它传递列表。因此,代码应该是这样的:

names1 = ("Jack", "Theo", "Jose")
names2 = ("Duncan", "Chris", "Anthony")
print(list(zip(names1,names2)))

运行代码,输出将是:

| [( 【杰克】 【邓肯】 )( 【西奥】 【克里斯】 )( 【何塞】【安东尼】)】 |

很明显,对列表和元组使用 zip()函数实际上没有区别。方便!

使用 zip()和字典

虽然您可以将 zip()与字典一起使用,但您需要注意一些限制。

让我们通过一个例子来深入了解本质:

names1 = {"mom":"Sonia", "dad":"Jesse"}
names2 = {"firstChild":"Andrew", "secondChild": "Kayla"}
print(list(zip(names1,names2)))

花一分钟思考一下代码输出应该是什么。

默认情况下zip()函数将字典的键缝合在一起。因此,运行代码将给出以下输出:

| [( 【妈妈】 【第一胎】 )( 【爸爸】 【二胎】 )] |

然而zip()函数也允许您“压缩”字典的值。要做到这一点,您必须在将 dictionary 对象传递给 zip()时调用它们的 values 方法。

让我们继续前面的例子,看看如何做到这一点:

names1 = {"mom":"Sonia", "dad":"Jesse"}
names2 = {"firstChild":"Andrew", "secondChild": "Kayla"}
print(list(zip(names1.values(),names2.values())))

代码的输出现在看起来像这样:

| 【(【索尼娅】【安德鲁】)【杰西】【凯拉】)】 |

从 zip()返回不同的数据结构

zip()的一个好处是,除了允许你传递不同的数据结构,它还允许你导出不同的数据类型。

向后滚动一点,回想一下 zip()的默认输出是一个特殊的 zip 对象。

之前,我们在 list 函数中包装了 zip()函数来输出一个列表。所以,您也可以使用 dict 和 tuple 来返回字典和元组,这并不奇怪。

我们来看一个例子:

names1 = ("Jack", "Theo", "Jose")
names2 = ("Duncan", "Chris", "Anthony")
print(tuple(zip(names1,names2)))

运行这段代码的结果看起来会像这样:

| (【杰克】 、 【邓肯】 )、( 【西奥】 、 【克里斯】 )、( 【何塞】 、 【安东尼】) |

我们也可以使用 dict 函数,就像这样

names1 = ("Jack", "Theo", "Jose")
names2 = ("Duncan", "Chris", "Anthony")
print(dict(zip(names1,names2)))

输出将如下所示:

| 【杰克】 : 【邓肯】【西奥】 : 【克里斯】【何塞】 : 【安东尼】 } |

使用带有两个以上参数的 zip()函数

本指南中的所有示例都包含两个可迭代变量,但是 zip()理论上可以使用无限个变量。在这一节中,我们将探索 zip()函数的这一功能。

让我们从定义三个变量来创建一个 zip 对象开始:

india_cities = ['Mumbai', 'Delhi', 'Bangalore']
africa_cities = ['Lagos', 'Cape Town', 'Tunis']
russia_cities = ['Moscow', 'Samara', 'Omsk']

现在,将三个对象压缩在一起就像将它们传递到 zip()中一样简单,就像这样:

print(list(zip(india_cities, africa_cities, russia_cities)))

代码的输出将如下所示:

| (【孟买】 【拉各斯】【莫斯科】 ) 【德里】【开普敦】【萨马拉】 【班加罗尔】 |

显然,向 zip()传递三个参数会创建一个新的数据结构。该结构的元素是长度为 3 的元组。

请记住,这延伸到更大的数字。简单地说,如果您向 zip()传递“n”个参数它将创建一个新的数据结构其中包含长度为“n”的元组元素

zip()如何处理不同长度的参数

在本指南中,我们只涉及了 zip()函数如何处理相同长度的数据结构。首先,我们处理了长度为 2 的结构,在上一节中,处理了长度为 3 的列表。

但是当我们将不同长度的参数传递给 zip()函数时会发生什么呢?

Python zip()函数非常灵活,接受不同长度的参数。让我们在这一节中探索函数的这一能力。

如果你回过头来打开 zip()函数的官方 Python 文档,函数的定义会让事情变得清楚。功能描述说:

“当最短的输入 iterable 用尽时,迭代器停止。”

换句话说zip()函数输出的长度将总是等于 最小的 自变量的长度。

这里有一个例子来验证这一点。假设我们有两组人:一组是男人,一组是女人。你想让他们两人一组,这样他们就可以上双人舞蹈课——也许是萨尔萨舞。

现在,让我们用 zip()函数写一些代码,看看它如何帮助我们:

menGroup = ['Devin', 'Bruce', 'Jimmy', 'Eddie']
womenGroup = ['Gina', 'Mila', 'Friday']
print(list(zip(menGroup,womenGroup)))

很明显menGroup 变量比 womenGroup 变量多包含一个元素。因此,我们可以预期 zip()函数会删除 menGroup 变量中的最后一个元素。

运行这段代码,我们将得到以下结果:

| [( 【德文】 【吉娜】 )( 【布鲁斯】 【米拉】 )( 【吉米】【星期五】) |

不出所料,埃迪没有得到萨尔萨舞比赛的参赛资格。让我们期待另一位女士的加入吧!

用 zip()函数循环多个对象

zip()函数最有用的一个方面是它允许你并行迭代多个对象。更重要的是,所涉及的语法容易记忆和使用!

我们用一个例子来理解用 zip()循环。假设你们两个列表:一个有学生的名字,另一个有成绩。下面是代码的样子:

studentList = ['Devin', 'Bruce', 'Jimmy'] 
gradeList = [97, 71, 83]

在本例中,让我们假设学生列表的排序位置与他们的成绩列表相同。现在,让我们编写代码来打印每个学生的姓名及其相应的成绩。

for student, grade in zip(studentList, gradeList):
    print(f"{student}'s grade is {grade}")

运行这段代码的结果如下:

| 德文的成绩是 97布鲁斯的成绩是 71 分吉米的成绩是 83 分 |

Python 3 和 2 在 zip()函数上的区别

需要注意的是zip()在 Python 2 和 3 中的工作方式不同。zip()函数返回 Python 2 中的元组列表,这个列表被截断为最短输入 iterable 的长度。

因此,如果您调用一个没有参数的 zip()函数,输出将是一个空列表,如下所示:

zipping = zip(range(3), 'WXYZ')
zipping  # Holding the list object
[(0, 'W'), (1, 'X'), (2, 'Y')]
type(zipping)
zipping = zip()  # Creating empty list
zipping
[]

正如所料,在上面的代码中调用 zip()会给出一个元组列表的输出。列表在“Y”处被截断不带参数调用 zip()会返回一个空列表。

但是在 Python 3 中 zip()并不是这样工作的。在 Python 3 中,不带参数调用 zip()会返回一个迭代器。

如果开发者需要,迭代器对象产生元组。但是元组只能被遍历一次。迭代以 StopIteration 异常结束,该异常在最短的输入 iterable 用尽时引发。

请记住,虽然 Python 3 中返回了迭代器,但它是空的。我们来看一个例子:

zipping = zip(range(3), 'WXYZ')
zipping  # Holding an iterator
type(zipping)
list(zipping)
zipping = zip()  # Create an empty iterator
zipping
next(zipping)

运行这段代码将产生输出:

| 回溯(最近呼叫最后一次):文件<字符串>,行 7 < 模块 >停止迭代 |

在这种情况下,调用 zip()函数会返回一个迭代器。第一次迭代在 c 处被截断,而第二次迭代引发 StopIteration 异常。

Python 3 的好处在于它可以模拟 Python 2 中 zip()的行为。它通过将返回的迭代器封装在对 list()的调用中来实现这一点。这样,它遍历迭代器,返回一个元组列表。

如果您使用的是 Python 2请注意向 zip()函数传递长输入 iterables 会导致不合理的高内存消耗。因此,如果您需要传递长输入的 iterables请使用 itertools.izip(*iterables)。

该函数生成一个迭代器,从可迭代对象中聚集元素。因此,使用它与在 Python 3 中使用 zip()具有相同的效果。我们来看一个例子:

from itertools import izip
zipped = izip(range(3), 'WXYZ')
zipped
list(zipped)

在上面的代码中,调用了 itertools.izip()函数,它创建了一个迭代器。当返回的迭代器被 list()消耗时,它输出一个元组列表,就像 Python 3 中的 zip()函数一样。当它到达最短输入迭代器的末尾时,它停止。

如果你同时使用 Python 2 和 3并且喜欢用两种语言编写代码那么这里有一些你可以使用的代码:

try:
    from itertools import izip as zip
except ImportError:
    pass

在这段代码中izip()函数在 itertools 中是可用的。因此,在 Python 2 中,该函数将在别名 zip 下导入。否则,程序将引发一个 ImportError 异常,这样您就知道您使用的是 Python 3。

上面的代码使你能够在你写的所有代码中使用 zip()函数。当代码运行时Python 会自动选择正确的版本。

既然您已经理解了 ins 和 out of zip()函数,那么是时候打开指关节,喝杯咖啡,开始编写现实世界的解决方案了!