组合迭代器是一种提供构建块的工具,可提高代码效率。本介绍将向您展示 Python 中一些最有用的组合迭代器。

数数

在本文中,我想简单介绍一下 Python 中的组合迭代器。

数学意义上的组合学是关于计数的。它可以帮助我们计算某事物的排列数(一副牌有多少种可能的排列方式)或组合数(不同颜色的球有多少种独特的排列方式)。要做到这一点,我们需要一组对象来采取行动——需要迭代的东西。

在 Python 中,可迭代对象(通常称为 可迭代 )是数据组。您可能熟悉的一些常见可迭代对象是 列表、元组、集合 和 数组 ,您可以使用 for 循环对其进行迭代。这些可迭代对象通常填充整数值、浮点数或字符串。字符串本身是可迭代对象,因为您可以循环遍历其中的所有字符。一个相关概念是迭代器它是一个返回可迭代对象的下一个元素的对象。

将这两部分放在一起,我们最终得到了组合迭代器。它们可以帮助您计算事物:例如,列表中数字的不同组合或字符串的不同排列。itertools 模块提供了帮助您完成所有这些操作的功能,该模块随 Python 的默认安装一起提供。

在我们深入了解 Python 中的组合迭代器之前,值得仔细研究一下如何迭代可迭代对象。如果您是 Python 的完全初学者,请查看本 课程 ,该课程旨在满足没有编程经验的人的需求。

迭代器、可迭代对象和迭代

我们说过,可迭代对象是数据组——例如整数列表。但要获取列表中的各个元素,我们需要一个迭代器。如果您对详细信息感兴趣,请查看 Python 文档 。 我们可以定义一个包含一些整数值的列表,如下所示:

x = [1, 2, 3]

需要注意的是,执行此操作时,整个列表都会保存到内存中。要遍历此列表,标准方法是使用循环 for ,但还有另一种方法,即使用一些鲜为人知的 Python 内置函数,特别是 iter() next() 。您可以直接在方法中定义可迭代对象 iter() 并打印元素,如下所示:

>>> x_iterator = iter([1, 2, 3])
>>> print(next(x_iterator))
1
>>> print(next(x_iterator))
2
>>> print(next(x_iterator))
3
>>> print(next(x_iterator))
StopIteration

x_iterator 类型为 的 <class 'list_iterator'> 创建了一个 [1, 2, 3] 类型为 的 <class 'list'> 。这个迭代器可以看作是一个接一个的整数流。为了访问整数,我们使用内置 next() 方法对其进行迭代,每次迭代一个值。一旦访问,整数就会从流中移除,迭代计数将存储为内部变量,这允许迭代器在 next() 再次调用该方法时记住其位置。迭代完成后,它会引发异常 StopIteration ,因为所有元素都已被移除。 这意味着迭代器只能遍历一次。

在这个阶段,您可能想知道 for 循环与所有这些有何关系,因为迭代通常是这样进行的。事实上,循环 for 是一种迭代器。在执行 for 循环之前,会在后台创建一个迭代器对象,然后执行迭代,直到 StopIteration 出现异常。对于那些需要复习 for 循环的人,请查看 这篇 文章。因此,使用 for 循环,可以通过以下方式实现迭代:

>>> x_iterator = iter([1, 2, 3])
>>> for element in x_iterator:
...    print(element)

请注意,我们必须重新定义, x_iterator 因为我们已经 StopIteration 在第一个示例中遇到了。这就是 x 直接遍历列表和遍历 x_iterator 。整个列表 x 存储在内存中,可以反复遍历,而是 x_iterator 一个整数流,只能遍历一次。因此,使用 x_iterator 更有效率,这在处理大量数据时确实开始发挥作用。

itertools 迭代器

顾名思义,该 itertools 在此处 找到文档 。此模块中有许多函数,它们都属于以下三个类别之一:无限迭代器(想象一个 while 循环)、终止迭代器(想象一个 for 循环)和组合迭代器(计数事物)。

它们被设计为内存高效,因此此模块中的函数返回迭代器,以数据流的形式提供结果。由于数据仅在需要时生成,因此可迭代对象不需要存储在内存中。这可能有点令人困惑,所以让我们看一些具体的例子,了解如何调用这些函数并检索结果。我们将要介绍的函数属于组合函数,可用于提高代码效率。

产品()

我们将要介绍的 itertools 第一个 product() ,它实现了两个可迭代对象的笛卡尔积。下图说明了它的工作原理,相当于从两个一维向量创建一个二维数组。输入可以是任何可迭代对象,输出则以元组列表的形式给出。如果您想要一个真正的数组,则必须重新转换输出,例如使用 NumPy。

Cartesian product of (x, y, z) x (1, 2, 3)

(x, y, z) x (1, 2, 3) 的笛卡尔积

要在 Python 中实现这一点,只需调用下面的函数即可 itertools

>>> result = itertools.product(['x', 'y', 'z'], [1, 2, 3])

结果变量现在是一个类型为 的迭代器 <class 'itertools.product'> 。这本质上与 x_iterator 前面示例中的 相同,因此,只能使用 for 循环或 方法迭代一次 next() 。或者,您可以将其重新转换为列表,此时整个结果 存储在内存中,可以多次迭代 .

>>> result_list = list(result)

还要注意,输入是字符串列表和整数列表。生成的元组列表保留这些数据类型。此函数还可用于计算可迭代对象与其自身的笛卡尔积,使用可选的 repeat 参数。以下两行给出相同的结果:

>>> itertools.product(['x', 'y', 'z'], repeat=2)
>>> itertools.product(['x', 'y', 'z'], ['x', 'y', 'z'])

尝试不使用 itertools 库来解决这个问题,看看你能得出什么结论。最明显的解决方案是使用两个 for 循环,并循环遍历两个列表中的每个元素,这需要 3 行代码。相反,使用函数可以更有效地解决这个简单的问题 product()

排列()

排列是按特定顺序排列对象。还记得介绍中的一副牌的例子吗?一副 52 张牌中有多少种不同的排列方式?它们是什么样子的?

实际上,用 Python 计算这些排列并不简单。对于 52 张牌,就有 52!(大约 8 x 10 67 )种排列。这个数字非常大,以至于每当你拿起一副洗好的牌时,你手中可能拿着一种以前从未存在过、以后也不会再存在的排列!所以请不要尝试在家里计算这个数字——如果你这样做,你的电脑不会感谢你的。

考虑一个更容易处理的问题,我们可以用 Python 计算排列。三种不同颜色的球有多少种可能的排列方式,它们看起来是什么样子的?

>>> balls = itertools.permutations(['red', 'green', 'blue'])
>>> for permutation in balls:
...     print(permutation)
...
('red', 'green', 'blue')
('red', 'blue', 'green')
('green', 'red', 'blue')
('green', 'blue', 'red')
('blue', 'red', 'green')
('blue', 'green', 'red')

有 3!= 3 x 2 x 1 = 6 种排列。也可以通过将球重新转换为列表并使用 len() 内置函数获取长度来计算。自己尝试一下。如果您想了解有关 Python 中一些最有用的内置函数的更多信息,请查看本 course .

组合()

下一个函数提供了在 Python 中计算组合的功能。这与排列略有不同,因为在考虑组合时项目的顺序并不重要。还有一个额外的关键字参数, r 它定义要查找的组合的长度。

让我们再看一下彩色球的例子,并将黄色球添加到列表中:

>>> balls = itertools.combinations(['red', 'green', 'blue', 'yellow'], r=3)

关键字 r=3 表示我们感兴趣的是考虑 3 个球的组合,其中有 4 个,如下所示:

>>> for combination in balls:
...     print(combination)
...
('red', 'green', 'blue')
('red', 'green', 'yellow')
('red', 'blue', 'yellow')
('green', 'blue', 'yellow')

组合与替换()

顾名思义,下一个函数与 类似 combinations() ,但它允许项目 重复多次 。这会产生更多可能的组合,因此我们只在下面显示一个子集,但请自行查看完整列表:

>>> balls = itertools.combinations_with_replacement(['red', 'green', 'blue', 'yellow'], r=3)
>>> for combination in balls:
...     print(combination)
...
('red', 'red', 'red')
('red', 'red', 'green')
('red', 'red', 'blue')
...
('blue', 'blue', 'yellow')
('blue', 'yellow', 'yellow')
('yellow', 'yellow', 'yellow')

编码挑战

上面用彩色球的例子演示了一些 itertools 函数是如何工作的,但它们有点枯燥。所以,是时候举一个更相关的例子了。

当你申请编程工作时,招聘经理通常会向申请人发送编码挑战以测试他们的技能。对于那些正在寻找技术工作的人来说, 这是 一篇有用的文章。让我们考虑一下你在下一次求职时可能会遇到的一个更有趣的问题,看看如何 itertools 应用。

问题陈述: “使用任意数量的 50 美元、20 美元和 10 美元钞票,有多少种方法可以兑换 100 美元钞票?”

一种简单的方法是手动生成 2 张钞票、3 张钞票、4 张钞票等的组合,并检查它们的总和是否为 100。这很容易出错,看起来像是一堆 for 循环、 while 循环和 if-else 语句。但认识到钞票的最大可能数量是 10 张(10 美元 x 10 = 100 美元),并且短语“任何数字”都意味着替换,您可以生成一个更有效的解决方案,如下所示:

>>> notes = [50, 20, 10]
>>> result = []
>>> for i in range(1, 11):
...     for combination in itertools.combinations_with_replacement(notes, i):
...         if sum(combination) == 100:
...             result.append(combination)
...
>>> print(len(result))
10

正如我们所展示的,使用 itertools 可以帮助计算 Python 中的笛卡尔积、排列和组合。它通过减少对循环和条件语句的依赖,大大简化了您的代码。原本需要多行才能完成的事情,现在只需一行即可完成。这使得代码更简单、更易读、更高效。

这已经是胜利了,但当你开始使用 itertools 函数作为构建块来为更复杂的基于迭代的算法创建组合表达式时,下一个层次就到来了。Python 还有一些内置迭代器,可以与它们结合使用来 itertools 实现下一个级别的编程。

您想要更多 Itertools 吗?

我们只讨论了这个库中的 4 个函数。值得一看的文档 itertools 可以 更好地了解这个库可以做什么。如果您有兴趣了解更多 itertools ,请尝试恰当命名的 more-itertools 模块。这个模块不随 Python 提供 - 因此您必须自己安装它,但它充满了有用的函数,在您的 Python 旅程中,这些函数肯定会让您的生活更轻松。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部