OldSchool
OldSchool

Reputation: 469

Python: How to pass a list of iterables to zip when the list is only determined at runtime?

I would like to use zip to iterate in lock step over the elements of various generators. Which generators and how many of them there will be is only known at run time. However, I can't work out how to pass this into zip as any list will be interpreted as an iterable rather than the list of iterables which must be simultaneously iterated over.

What's the correct way to do this? Also, what's the correct way to unpack the outputs when their number isn't known until runtime? Below is what I have tried

from typing import List

l_1 = [1, 2, 3]
l_10 = [10, 20, 30]
l_100 = [100, 200, 300]

def gen(l_: List):
    for i in l_:
        yield i

# Standard: works fine as expected
for i, j, k in zip(gen(l_1), gen(l_10), gen(l_100)):
    print('i={}, j={} k={}'.format(i,j,k))

# List of generators prepared at run time. Does not work
gens = [gen(l_1), gen(l_100)]
receive = [None] * len(gens)
for receive in zip(gens):
    print(receive)

The output is

i=1, j=10 k=100
i=2, j=20 k=200
i=3, j=30 k=300
(<generator object gen at 0x000001C3DB93C258>,)
(<generator object gen at 0x000001C3DB93C620>,)

The desired output is

i=1, j=10 k=100
i=2, j=20 k=200
i=3, j=30 k=300
[1, 100]
[2, 200]
[3, 300]

How can I get zip to recognize that gens is not an iterable to iterate over, but rather the list of iterables which must be iterated over in lock step?

Upvotes: 2

Views: 502

Answers (1)

Queuebee
Queuebee

Reputation: 670

It will be only a small change, to pass a list of iterables to zip you have to unpack it using *

# List of generators prepared at run time. Does not work
gens = [gen(l_1), gen(l_100)]
receive = [None] * len(gens)
for receive in zip(*gens):
    print(list(receive))

edit:
since you expected lists in your output I had to explicitly convert receive to a list.

Upvotes: 5

Related Questions