I'm trying to figure out why my nested list comprehension doesn't work. Code:
def myzip(*args):
try:
x = [iter(i) for i in [*args]]
while True:
# for i in range(5):
yield [next(ii) for ii in x]
# I want to know why the commented line below won't work
# yield [next(ii) for ii in [iter(i) for i in [*args]]]
# or why this outputs [1, 'a']
# return [next(ii) for ii in [iter(i) for i in [*args]]]
except:
pass
def main():
print(list(myzip([1, 2, 3, 4, 5], ['a', 'b', 'c'])))
if __name__ == '__main__':
main()
if I assign [iter(i) for i in [*args]]
to x
and then use this list comprehension [next(ii) for ii in x]
everything works ok. and the output will be:
[[1, 'a'], [2, 'b'], [3, 'c']]
But if I try to ommit variable x
and do it in one bigger list comprehension (commented line) [next(ii) for ii in [iter(i) for i in [*args]]]
it goes in infinite loop. If you replace infinite loop with the for loop (commented line), the output is:
[[1, 'a'], [1, 'a'], [1, 'a'], [1, 'a'], [1, 'a']]
Moreover, if I try return [next(ii) for ii in [iter(i) for i in [*args]]]
it just returns:
[1, 'a']
Can someone tell me why is that?
so i'm not going to answer your question directly (as the comments pretty well have this covered), but i am going to show how this can be done as a horrible one liner, just for fun:
def my_zip(*args):
yield from iter(lambda data=[iter(a) for a in args]: [next(d) for d in data], None)
there are three parts.
iter
, after yield from
, creates a callable_iterator
. this will repeat calling its first argument (the lambda
) until it either sees its second argument (the sentinel value, in this case None
) OR StopIteration
errorStopIteration
exception.lambda
i have a default value for data
which is a list of the iterators you want to iterate over. this gets created once, so each subsequent call to the function will reference this.lambda
, i manually advance each of the iterators in data
one time per function call. this will continue until one of the iterators is exhausted, at which point a StopIteration
will be raised. iter
call will bail as soon as it gets its StopIteration
list
consumer which causes it to, well, stop iterating. because of the exception, the sentinel value doesn't matter.finally: please never actually do this in real code.
edit: some sample output
In [90]: list(my_zip('abcd', [1,2,3]))
Out[90]: [['a', 1], ['b', 2], ['c', 3]]
In [91]: list(my_zip('abcd', [1,2,3,4,5,6]))
Out[91]: [['a', 1], ['b', 2], ['c', 3], ['d', 4]]
explanation edits: hat tip to @superb rain for their smart comments below.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With