如何解压迭代器?

问题描述 投票:0回答:4

给定一个对列表

xys
,将其解压为两个列表的 Python 习惯用法是:

xs, ys = zip(*xys)

如果

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

python iterator generator python-itertools
4个回答
12
投票

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

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)

注意这将保留您迭代其中一个与另一个之间的“差异”。


8
投票

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

这样的东西允许您迭代成对的“左侧项目”和“右侧项目”:

 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()
。)


0
投票

完整答案位于此处。长话短说:我们可以修改

itertools.tee
函数 的 Python 配方,比如

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

0
投票

以下函数本质上执行与

zip
函数相反的操作。

def unzip(iter, n=2):
    iter_copies = itertools.tee(iter, n)
    def _gen(i):
        for x in iter_copies[i]:
            yield x[i]
    indv_iters = []
    for i in range(n):
        indv_iters.append(_gen(i))
    return tuple(indv_iters)

这是一个更紧凑的版本:

def unzip(iter, n=2):
    iter_copies = itertools.tee(iter, n)
    indv_iters = [(lambda i: (x[i] for x in iter_copies[i]))(i) for i in range(n)]
    return tuple(indv_iters)

您可以使用以下代码验证它是否有效:

def sample_generator():
    for i, j, k in zip(range(10), range(10, 20), range(20, 30)):
        yield i, j, k

i_iter, j_iter, k_iter = unzip(sample_generator(), 3)

for i, j, k, reference in zip(i_iter, j_iter, k_iter, range(10)):
    assert i == reference
    assert j == reference + 10
    assert k == reference + 20
© www.soinside.com 2019 - 2024. All rights reserved.