[Python-ideas] Add 'interleave' function to itertools?

MRAB python at mrabarnett.plus.com
Wed Aug 7 20:47:16 CEST 2013


On python-list at python.org there was a thread "make elements of a list
twice or more." where the OP wanted to repeat items in a list. For
example, given:

     [b, a, c]

the output should be:

     [b, b, a, a, c, c]

It occurred to me that this was just a special case of interleaving
iterables.

Going the opposite way with a list is simple enough:

 >>> numbers = [0, 1, 2, 3, 4, 5]
 >>> evens = numbers[0 : : 2]
 >>> odds = numbers[1 : : 2]
 >>> evens
[0, 2, 4]
 >>> odds
[1, 3, 5]

but how do you interleave them again?

 >>> [number for pair in zip(evens, odds) for number in pair]
[0, 1, 2, 3, 4, 5]

I'm suggesting adding an 'interleave' function to itertools:

 >>> list(interleave(evens, odds))
[0, 1, 2, 3, 4, 5]

The function would stop when any of the iterables became exhausted
(compare 'zip').

There could also be a related 'interleave_longest' function (compare
'zip_longest').

Here are the definitions:

def interleave(*iterables):
     """Return a interleave object whose .__next__() method returns an
     element from each iterable argument in turn.  The .__next__()
     method continues until the shortest iterable in the argument
     sequence is exhausted and then it raises StopIteration.
     """

     sources = [iter(iterable) for iterable in iterables]

     try:
         while True:
             for iterable in sources:
                 yield next(iterable)
     except StopIteration:
         pass

def interleave_longest(*iterables, fillvalue=None):
     """Return an interleave_longest object whose .__next__() method
     returns an element from each iterable argument in turn.  The
     .__next__() method continues until the longest iterable in the
     argument sequence is exhausted and then it raises StopIteration.
     When the shorter iterables are exhausted, the fillvalue is
     substituted in their place.  The fillvalue defaults to None or can
     be specified by a keyword argument.
     """

     sources = [iter(iterable) for iterable in iterables]

     if not sources:
         return

     remaining = len(sources)

     while True:
         for index, iterable in enumerate(sources):
             try:
                 yield next(iterable)
             except StopIteration:
                 remaining -= 1
                 if not remaining:
                     return

                 sources[index] = repeat(fillvalue)

                 yield fillvalue


More information about the Python-ideas mailing list