How to unzip an iterator?

后端 未结 3 648
南方客
南方客 2021-02-19 10:45

Given a list of pairs xys, the Python idiom to unzip it into two lists is:

xs, ys = zip(*xys)

If xys is an iterator,

相关标签:
3条回答
  • 2021-02-19 10:50

    If you want to consume one iterator independently from the other, there's no way to avoid pulling stuff into memory, since one of the iterators will progress while the other does not (and hence has to buffer).

    Something like this allows you to iterate over both the 'left items' and the 'right items' of the pairs:

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

    ...but keep in mind that if you consume only one iterator, the other will buffer items in memory until you start consuming them.

    (Btw, assuming Python 3. In Python 2 you need to use itertools.imap(), not map().)

    0 讨论(0)
  • 2021-02-19 10:58

    Suppose you have some iterable of pairs:

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

    If I'm correctly interpreting what you are asking for, you could generate independent iterators for the firsts and seconds using itertools.tee:

     xs, ys = itertools.tee(a)
     xs, ys = (x[0] for x in xs), (y[1] for y in ys)
    

    Note this will keep in memory the "difference" between how much you iterate one of them vs. the other.

    0 讨论(0)
  • 2021-02-19 11:03

    The full answer locates here. Long story short: we can modify Python recipe for itertools.tee function like

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

    and then use it

    >>> from itertools import count
    >>> zipped = zip(count(), count())
    >>> xs, ys = unzip(zipped)
    >>> next(xs)
    0
    
    0 讨论(0)
提交回复
热议问题