【问题标题】:Does chunking via itertools vs straight python buy you anything extra?通过 itertools 与直接 python 进行分块是否会给您带来额外的好处?
【发布时间】:2026-02-08 15:45:02
【问题描述】:

我遇到two different waysiterable 拆分为“块”(超过一项)。

One method uses itertools:

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

the other method is straight python:

def chunker(seq, size):
    return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))

itertools 实现会为您购买任何“额外”的东西吗?

“额外”在哪里,可能更快、更灵活或更安全。

我问是因为这里显示的 itertools 实现绝对不是 IMO 更具可读性/直观性。

【问题讨论】:

  • 我同意可迭代方法看起来有点神奇,但它也可以以更易读的方式实现。迭代器和切片这两种方法都可以用多种方式编写,因此可读性并不是真正的考虑因素 imo
  • 您也可以使用:def chunker(iterable, size): yield from iter(lambda it=iter(iterable): list(islice(it, size)), [])...
  • @unutbu 是的,并且在yield from 之前也能很好地工作......唯一明显的区别是inspect.isgeneratoriter 失败......但是耸耸肩 ... 6 个半打等等...
  • @Felk 我认为“magic-y”是一种委婉说法。通过乘数引用滥用列表初始化的文档:“它们被多次引用。这经常困扰新的 Python 程序员”。这其中的坏处是几乎相同的[iter(iterable) for _ in range(n)] 具有不同的效果。

标签: python itertools readability


【解决方案1】:

grouper 可以与 any iterable 一起使用——包括生成器和无限迭代器。 chunker 只能与 sequences 一起使用——它的长度是预先知道的。

from itertools import izip_longest

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

def chunker(seq, size):
    return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))

x = (i**2 for i in range(5))

print(list(grouper(x, 3)))
# [(0, 1, 4), (9, 16, None)]

print(list(chunker(x, 3)))
# TypeError: object of type 'generator' has no len()

【讨论】:

    【解决方案2】:

    这两个功能不等价。有几个区别:

    • 您的grouper 将适用于任何可迭代对象,包括迭代器和生成器,而chunker 需要支持索引的序列([...])。

      >>> it = lambda : (i for i in range(6))  # creates a generator when called
      >>> list(grouper(it(), 3))
      [(0, 1, 2), (3, 4, 5)]
      >>> list(chunker(it(), 3))
      TypeError: object of type 'generator' has no len()
      

      请注意,其他答案已经提到了这一点!

    • 如果块大小不是长度的除数,chunkers 最后一个元素将小于块大小。 OTOH grouper 将用一些填充值填充它。同样chunker 将返回与原始类型相同的类型,而grouper 将返回tuples:

      >>> list(grouper([1,2,3,4,5], 3))
      [(1, 2, 3), (4, 5, None)]
      >>> list(chunker([1,2,3,4,5], 3))
      [[1, 2, 3], [4, 5]]
      
    • grouper 使用高性能的内置函数iterzip_longest。这些将非常快。以牺牲可读性为代价。但是,这可以使它比chunker 快得多:

      l = list(range(10000))
      %timeit list(grouper(l, 10))
      # 320 µs ± 6.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      %timeit list(chunker(l, 10))
      # 1.22 ms ± 19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      

    所以grouper 是比chunker 更快、更通用的方法。但是,根据具体情况,使用chunker 可能更有用,例如,如果您不喜欢“填充”部分或想要保留“块”的类型。

    【讨论】: