代码之家  ›  专栏  ›  技术社区  ›  BCS

python生成器,将另一个iterable分组为n组[重复]

  •  15
  • BCS  · 技术社区  · 14 年前

    我在找一个函数 i 一个尺寸 n 并产生长度的元组 n 这是从 :

    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 回复  |  直到 6 年前
        1
  •  19
  •   Community CDub    7 年前

    grouper 食谱 docs for the itertools package

    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 )

        2
  •  19
  •   Community CDub    7 年前

    当您想将迭代器分组为 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]]
    

    下半年有一个关于它如何工作的解释。 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')]
    

    当你想要A组的时候 序列 大块 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__ 定义。

        3
  •  4
  •   Skurmedel    9 年前

    这个怎么样?但是它没有填充值。

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

    它使用了一个原始的iterable的副本,对于每个连续的拼接,它都会消耗这个副本。我疲倦的大脑唯一能想到的另一种方法是产生范围内的拼接端点。

    也许我该换一下 list() tuple() 所以它更适合于你的输出。

        4
  •  2
  •   Mahmoud Hashemi    9 年前

    这是Python中非常常见的请求。足够普通,它使它成为 boltons 统一实用程序包。首先, there are extensive docs here . 此外, the module 它的设计和测试只依赖于标准库(与Python2和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多个博尔顿食谱)有帮助!

        5
  •  2
  •   Jason R. Coombs    8 年前

    我用 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,)]
    
        6
  •  1
  •   Gecko    8 年前

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

    def chunks(n, iterator):
        out = []
        for elem in iterator:
            out.append(elem)
            if len(out) == n:
                yield out
                out = []
        yield out
    
        7
  •  0
  •   fungs    9 年前

    我知道这已经回答了好几次了,但是我正在添加我的解决方案,这两个解决方案都应该改进:序列和迭代器的一般适用性,可读性(StopIteration异常不存在不可见的循环退出条件)和与Grouper配方相比的性能。这与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
  •   Carles Sala    9 年前

    这里有一个不同的解决方案,它不使用itertools,即使它有更多的行,当块比iterable length短得多时,它显然优于给定的答案。 然而,对于大块头来说,其他的答案要快得多。

    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
    
        9
  •  0
  •   OrangeDog    6 年前
        def grouper(iterable, n):
            while True:
                yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))