Python生成器,将另一个可迭代组合成N组

时间:2010-10-21 23:12:28

标签: python generator std

我正在寻找一个带有可迭代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 个答案:

答案 0 :(得分:19)

请参阅docs for the itertools package

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

(但是,这是quite a few questions的副本。)

答案 1 :(得分:19)

如果要将n 的块中的迭代器分组,而不填充最后一个具有填充值的组,请使用iter(lambda: list(IT.islice(iterable, n)), []) :< / p>

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]]

有一个解释说明它在this answer的后半部分是如何工作的。

如果要将迭代器分组为n 并填充最后一个具有填充值的组,请使用grouper recipe 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')]

如果要将{strong>序列分组为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]

请注意,与通常的迭代器不同,sequences by definition具有长度(即__len__已定义)。

答案 2 :(得分: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(),以便更好地与您的输出相对应。

答案 3 :(得分:3)

这是Python中非常常见的请求。足够常见,它使它成为boltons统一实用程序包。首先,there are extensive docs here。此外,the module的设计和测试仅依赖于标准库(Python 2和3兼容),这意味着您可以just download the file directly into your project

# if you downloaded/embedded, try:
# from iterutils import chunked

# with `pip install boltons` use:

from boltons.iterutils import chunked 

print(chunked(range(10), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

还有一个用于不定/长序列的迭代器/生成器形式:

print(list(chunked_iter(range(10), 3, fill=None)))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]]

如您所见,您也可以使用您选择的值填充序列。最后,作为维护者,我可以向您保证,虽然代码已被数千名开发人员下载/测试,但如果您遇到任何问题,您将通过boltons GitHub Issues page获得最快的支持。希望这个(和/或任何其他150+ boltons食谱)有所帮助!

答案 4 :(得分:2)

我使用chunked function from the more_itertools package

$ pip install more_itertools
$ python
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> [tuple(z) for z in more_itertools.more.chunked(x, 3)]
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (0,)]

答案 5 :(得分:1)

这是一个非常古老的问题,但我认为对于一般情况提及以下方法是有用的。它的主要优点是它只需要迭代一次数据,因此它可以用于数据库游标或只能使用一次的其他序列。我也发现它更具可读性。

def chunks(n, iterator):
    out = []
    for elem in iterator:
        out.append(elem)
        if len(out) == n:
            yield out
            out = []
    yield out

答案 6 :(得分:0)

    def grouper(iterable, n):
        while True:
            yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))

答案 7 :(得分:0)

我知道这已经被多次回答了,但是我正在添加我的解决方案,它应该在两者中都有所改进,对序列和迭代器的一般适用性,可读性(StopIteration异常没有隐形循环退出条件)和与石斑鱼配方相比的性能。这与Svein的最后一个答案最为相似。

def chunkify(iterable, n):
    iterable = iter(iterable)
    n_rest = n - 1

    for item in iterable:
        rest = itertools.islice(iterable, n_rest)
        yield itertools.chain((item,), rest)

答案 8 :(得分:0)

这是一个不使用itertools的不同解决方案,即使它有更多行,但是当块比可迭代长度短很多时,它显然优于给定的答案。 但是,对于大块,其他答案要快得多。

def batchiter(iterable, batch_size):
    """
    >>> list(batchiter('ABCDEFG', 3))
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
    """
    next_batch = []
    for element in iterable:
        next_batch.append(element)
        if len(next_batch) == batch_size:
            batch, next_batch = next_batch, []
            yield batch
    if next_batch:
        yield next_batch


In [19]: %timeit [b for b in batchiter(range(1000), 3)]
1000 loops, best of 3: 644 µs per loop

In [20]: %timeit [b for b in grouper(3, range(1000))]
1000 loops, best of 3: 897 µs per loop

In [21]: %timeit [b for b in partition(range(1000), 3)]
1000 loops, best of 3: 890 µs per loop

In [22]: %timeit [b for b in batchiter(range(1000), 333)]
1000 loops, best of 3: 540 µs per loop

In [23]: %timeit [b for b in grouper(333, range(1000))]
10000 loops, best of 3: 81.7 µs per loop

In [24]: %timeit [b for b in partition(range(1000), 333)]
10000 loops, best of 3: 80.1 µs per loop