将可迭代对象按N分组的Python生成器

24 投票
9 回答
13116 浏览
提问于 2025-04-16 05:53

我在找一个函数,这个函数可以接收一个可迭代的对象 i 和一个大小 n,然后返回长度为 n 的元组,这些元组里的值是从 i 中顺序取出来的:

x = [1,2,3,4,5,6,7,8,9,0]
[z for z in TheFunc(x,3)]

返回的结果是

[(1,2,3),(4,5,6),(7,8,9),(0)]

标准库里有没有这样的函数呢?

如果标准库里有这个函数,我找了半天也没找到,搜索的关键词也用尽了。我可以自己写一个,但我不太想这么做。

9 个回答

4

这个怎么样?不过它没有填充值。

>>> def partition(itr, n):
...     i = iter(itr)
...     res = None
...     while True:
...             res = list(itertools.islice(i, 0, n))
...             if res == []:
...                     break
...             yield res
...
>>> list(partition([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>

它使用了原始可迭代对象的一个副本,每次进行切片操作时都会用完这个副本。我脑子里能想到的唯一其他方法就是用范围生成切片的结束点。

也许我应该把 list() 改成 tuple(),这样更符合你的输出。

34

当你想把一个迭代器分成每组 n 个元素,并且不填充最后一组的空值时,可以使用 iter(lambda: list(IT.islice(iterable, n)), [])

import itertools as IT

def grouper(n, iterable):
    """
    >>> list(grouper(3, 'ABCDEFG'))
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
    """
    iterable = iter(iterable)
    return iter(lambda: list(IT.islice(iterable, n)), [])

seq = [1,2,3,4,5,6,7]
print(list(grouper(3, seq)))

这个方法会返回

[[1, 2, 3], [4, 5, 6], [7]]

在这段内容的后半部分,有对它是如何工作的解释,详见 这个回答


当你想把一个迭代器分成每组 n 个元素,并且填充最后一组的空值时,可以使用 grouper 这个方法 zip_longest(*[iterator]*n)

例如,在 Python2 中:

>>> list(IT.izip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]

在 Python3 中,之前的 izip_longest 现在改名为 zip_longest

>>> list(IT.zip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]

当你想把一个序列分成每组 n 个元素时,可以使用 chunks 这个方法:

def chunks(seq, n):
    # https://stackoverflow.com/a/312464/190597 (Ned Batchelder)
    """ Yield successive n-sized chunks from seq."""
    for i in xrange(0, len(seq), n):
        yield seq[i:i + n]

需要注意的是,与一般的迭代器不同,序列的定义是有长度的(也就是说 __len__ 是有定义的)。

20

请查看 itertools 包的文档中的 grouper 这个用法示例,具体可以在这里找到:itertools 包的文档

def grouper(n, iterable, fillvalue=None):
  "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
  args = [iter(iterable)] * n
  return izip_longest(fillvalue=fillvalue, *args)

(不过,这个问题其实和很多其他问题是重复的。)

撰写回答