如何解压缩迭代器?

How to unzip an iterator?

给定一个列表对 xys,将其解压缩到两个列表中的 Python 习惯用法是:

xs, ys = zip(*xys)

如果 xys 是一个迭代器,我如何将它解压缩为两个迭代器,而不将所有内容都存储在内存中?

如果您想独立于另一个迭代器使用一个迭代器,则无法避免将内容拉入内存,因为其中一个迭代器会进行,而另一个不会(因此必须进行缓冲)。

这样的事情可以让你迭代 'left items' 和 'right items' 对:

 import itertools
 import operator

 it1, it2 = itertools.tee(xys)
 xs = map(operator.itemgetter(0), it1))
 ys = map(operator.itemgetter(1), it2))

 print(next(xs))
 print(next(ys))

...但请记住,如果您只使用一个迭代器,另一个将在内存中缓冲项目,直到您开始使用它们。

(顺便说一句,假设 Python 3。在 Python 2 中你需要使用 itertools.imap(),而不是 map()。)

假设你有一些可迭代的对:

a = zip(range(10), range(10))

如果我正确解释了您的要求,您可以使用 itertools.tee:

为第一个和第二个生成独立的迭代器
 xs, ys = itertools.tee(a)
 xs, ys = (x[0] for x in xs), (y[1] for y in ys)

注意 这将在内存中保留 "difference" 你迭代其中一个与另一个之间的次数。

完整答案定位here. Long story short: we can modify Python recipe for itertools.tee function喜欢

from collections import deque


def unzip(iterable):
    """
    Transposes given iterable of finite iterables.
    """
    iterator = iter(iterable)
    try:
        first_elements = next(iterator)
    except StopIteration:
        return ()
    queues = [deque([element])
              for element in first_elements]

    def coordinate(queue):
        while True:
            if not queue:
                try:
                    elements = next(iterator)
                except StopIteration:
                    return
                for sub_queue, element in zip(queues, elements):
                    sub_queue.append(element)
            yield queue.popleft()

    return tuple(map(coordinate, queues))

然后使用它

>>> from itertools import count
>>> zipped = zip(count(), count())
>>> xs, ys = unzip(zipped)
>>> next(xs)
0