如何理解使用 izip_longest 对列表进行分块的代码?

Posted

技术标签:

【中文标题】如何理解使用 izip_longest 对列表进行分块的代码?【英文标题】:How to unserstand the code using izip_longest to chunk a list? 【发布时间】:2015-03-04 05:36:55 【问题描述】:

What is the most “pythonic” way to iterate over a list in chunks? 中的最佳答案使用函数 izip_longest 来分块列表。但是我看不懂。

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

for item in grouper(range(10), 4):
    print list(item)

我运行上面的代码,然后创建分块列表:

[1 ,2, 3, 4]
[5, 6, 7, 8]
[9, 10, None, None]

我尝试一步一步运行它:

In [1]: args = [iter(range(10))] * 4

In [2]: args
Out[2]: 
[<listiterator at 0x1ad7610>,
 <listiterator at 0x1ad7610>,
 <listiterator at 0x1ad7610>,
 <listiterator at 0x1ad7610>]

一个列表是由同一个迭代器创建的。我知道函数 izip_longest 是用来生成列表对的。 izip_longest 如何将迭代器转换为分块列表?谢谢。

【问题讨论】:

【参考方案1】:

grouper 函数只是用自身的偏移版本压缩原始迭代。使用[iter(iterable)] * n 创建一个列表,其中n 引用相同的迭代器。这些不是独立的副本;它们都是对同一个对象的引用,因此推进一个会推进它们。这是一个简单的例子:

>>> x = [1, 2, 3]
>>> a, b = [iter(x)] * 2
>>> next(a)
1
>>> next(b)
2

izip_longestzip 一样,每次从每个迭代器中获取一个元素。所以首先它从args 中的第一个迭代中获取第一个元素,这将是原始迭代的第一个元素。但是当它抓取这个元素时,它会推进所有的迭代器,因为它们都是链接的。所以当izip_longest从下一个迭代器中获取一个元素时,它会从原始迭代器中获取第二个元素。就这样继续下去;每次它从一个迭代器中抓取一个元素时,它都会推进所有这些元素,因此它从下一个迭代器中抓取的项目将是原始迭代器中的下一个项目。

【讨论】:

谢谢。我查找'izip_longest'的源代码docs.python.org/2/library/itertools.html#itertools.izip_longest,仍然对'iterators = [chain(it, sentinel(),fillers) for it in args]'这一行感到困惑,args 中的迭代器是如何链接的?跨度> @zfz:如果你想知道izip_longest 本身在内部是如何工作的,你应该问一个单独的问题。

以上是关于如何理解使用 izip_longest 对列表进行分块的代码?的主要内容,如果未能解决你的问题,请参考以下文章

未能导入名为“版本”的模块,因为 izip_longest

如何允许对不在编辑模式下的 SwiftUI 列表中的行进行重新排序?

在 Flutter 中对未来列表进行排序

如何使用简单的管理对列表中的多个字段进行排序?

如何使用 lambda 表达式对相同的列表进行排序?

我应该如何使用 scikit learn 对以下列表列表进行矢量化?