Search code examples
pythonpython-3.xiterable-unpacking

generate tuple of values from iterables in Python 3


If a function gets passed an undefined number of iterables N of arbitrary length M, is there a way to obtain a generator that yield M tuples of size N containing the elements of the passed iterables?

def dispatch(*iterables):
    args = ( some kind of generator that involves *iterables)
    for _ in args:
        yield _

In other words, if we consider *iterables as an NxM matrix in which each column (function argument) is an iterable is there a way using a generator to yield the rows of the matrix?

Eg:

a = [9,8,7,6]
b = 'ciao'
c = iter(range(0,4))

>>> res = dispatch(a,b,c)
>>> res.__next__()
(9,c,0)
>>> res.__next__()
(8,i,1)

etc...

furthermore since this function could also take only 1 iterable as argument, the generator should be able to handle the case and output something like:

a = [9,8,7,6]
>>> res = dispatch(a)
>>> res.__next__()
(9,)
>>> res.__next__()
(8,)

I tried using zip but it doesn't handle the aforementioned edge case, plus it is kind of slow, which suggest that it probably has to read the whole thing before producing an output.


Solution

  • You can use map combined with a lambda that pack the arguments:

    >>> list(map(lambda *x: tuple(x), range(10), range(10), range(10)))
    [(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9)]
    >>> list(map(lambda *x: tuple(x), range(10)))
    [(0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,)]
    

    Your function would be really simple:

    def dispatch(*args):
        return map(lambda *x: tuple(x), *args)
    

    As in your example:

    >>> a = [9,8,7,6]
    >>> b = 'ciao'
    >>> c = iter(range(0,4))
    >>> list(dispatch(a, b, c))
    [(9, 'c', 0), (8, 'i', 1), (7, 'a', 2), (6, 'o', 3)]