Python中的组合迭代器介绍


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,类型是

在这个阶段,你可能想知道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更有效率,当处理大量数据时,这才真正开始得到回报。

The itertools Iterators

如其名,itertools模块提供了处理iterables和iterators的工具。你可以在这里找到相关的文档。这个模块中有许多函数,它们都属于三类中的一类:无限迭代器(想想看while循环)、终止迭代器(想想看for循环)和组合迭代器(计算事物)。

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

product()

我们将关注的第一个 itertools 函数是 product(),它实现了两个迭代变量的笛卡尔乘积。它的工作原理如下图所示,相当于从两个一维向量创建一个二维数组。输入可以是任何可迭代的,而输出则是一个图元的列表。如果你想要一个真实的数组,你必须重新转换输出,例如使用NumPy。

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

要在Python中实现这一点,只需调用itertools的函数,如下所示:

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

结果变量现在是一个迭代器,类型为

>>> result_list = list(result)

注意,输入是一个字符串列表和一个整数列表。结果列表中的图元保持了这些数据类型。这个函数也可以用来计算一个迭代器与自身的笛卡尔积,使用可选的重复参数。下面两行给出了相同的结果:

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

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

尝试在没有itertools库的情况下解决这个问题,看看你能想到什么。最明显的解决方案是使用两个for循环,并在两个列表中的每个元素中循环,需要3行代码。

permutations()

permutation 是一种以特定顺序排列的对象,它可以更有效地解决这一简单问题。还记得介绍中的那副扑克牌的例子吗?在一副 52 张牌中存在多少种不同的排列方式,它们是什么样子的?

实际上,在 Python 中计算这些排列方式并不直接了当。对于 52 张牌,有 52! (大约是 8 x 1067) 种排列组合。这是一个非常大的数字,以至于当你拿起一副洗好的牌时,你所拿的排列方式可能是以前从未存在过的,而且以后也不会再有了!因此,请不要用Python计算。所以请不要尝试在家里计算这个问题,如果你这样做了,你的电脑不会感谢你的。

考虑一个更容易解决的问题,我们可以用Python计算排列组合。三个不同颜色的球有多少种可能的排列,它们看起来像什么?

>>> balls = itertools.permutations(['red', 'green', 'blue'])

>> for permutation in balls:

...     print(permutation)...('红'、'绿'、'蓝')('红'、'蓝'、'绿')('绿'、'红'、'蓝')('蓝'、'绿'、'红')

有3!=3×2×1=6种排列组合。这也可以通过将球重铸为一个列表并通过len()内置函数获得长度来计算。自己试试吧。如果你想了解更多关于Python中一些最有用的内置函数,请查看这个课程。

combinations()

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

让我们再看看我们的例子,用彩色的球,并在列表中添加一个黄色的球:

>>> balls = itertools.combinations(['红', '绿', '蓝', '黄'], 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_with_replacement()

正如其名称所示,下一个函数与 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') A 编码挑战

上面这些彩色球的例子展示了一些itertools函数如何工作,但它们有点枯燥。所以,现在是时候来个更有意义的例子了。

当你申请编程工作时,招聘经理通常会向申请人发送一个编码挑战,以测试他们的技能。对于那些正在寻找技术工作的人,这里有一篇有用的文章。

问题陈述:“使用任意数量的 50 美元、20 美元和 10 美元的纸币,你能以多少种方式改变一张 100 美元的纸币?”

一种天真的方法是手动生成 2 张纸币、3 张纸币、4 张纸币等的组合,并检查它们是否相加为 100。这很容易出错,而且看起来像一盘for循环、while循环和if-else语句的沙拉。但是认识到最大可能的纸币数量是10(10美元×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 结合使用,以实现编程的下一个层次。

你想要更多的迭代器吗?

我们只谈到了这个库中的 4 个函数。值得一看的是 itertools 文档,以更好地了解该库的功能。如果你有兴趣获得更多的itertools,可以尝试命名为more-itertools的模块。这个模块并不与 Python 一起出现,所以你必须自己安装它,但是它充满了有用的功能,肯定会在你的 Python 之旅中使你的生活更轻松。


本文标签

热门标签

会员评论