分块迭代,包括生成器

Chunking iterables, including generators

我有这个用于分块迭代的解决方案:

def chunks(items, chunk_size):
    def get_chunk():
        try:
            for _ in range(chunk_size):
                yield next(iterator)
        except StopIteration:
            return False

    iterator = iter(items)
    while chunk := list(get_chunk()):
        yield chunk


for c in chunks([1, 2, 3, 4, 5, 6, 7, 8], 3):
    print(c)

它运行良好,与我在 SO 上找到的其他一些解决方案不同,它还处理 'infinite' 生成器,例如:

def natural_numbers():
    n = 0
    while True:
        yield (n := n + 1)


tens = chunks(natural_numbers(), 10)
for _ in range(5):
    print(next(tens))

但是,我无法动摇这样的感觉,即无需调用内部函数也可以做到这一点。当然,您可以定义一个外部函数并传入 chunk_sizeiterator,这将避免在每次调用 chunks 时重新定义 get_chunk()。但是它仍然会有为每个块调用该函数的开销。

有没有人建议避免函数调用,但仍然适用于无法索引或切片的可迭代对象?

我使用该函数的主要原因是能够捕获 StopIteration,我认为这不能在生成器理解中完成而不丢失异常前的最后几项,但也许我错了。

使用 while 循环:

def chunks(items, chunk_size):
    iterator = iter(items)
    done = False
    while not done:
        chunk = []
        for _ in range(chunk_size):
            try:
                chunk.append(next(iterator))
            except StopIteration:
                done = True
                break
        if chunk:
            yield chunk

使用 for 循环:

def chunks(items, chunk_size):
    iterator = iter(items)
    chunk = []
    for element in iterator:
        chunk.append(element)
        if len(chunk) == chunk_size:
            yield chunk
            chunk = []
    if chunk:
        yield chunk

保留您的原始想法但删除嵌套函数:

from itertools import islice

def chunks(items, chunk_size):
    iterator = iter(items)
    while chunk := list(islice(iterator, chunk_size)):
        yield chunk

使用第 3 方库:

>>> from more_itertools import chunked
>>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3))
[[1, 2, 3], [4, 5, 6], [7, 8]]

如果我没记错的话,more-itertoolschunkedichunked

https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked