Premature ending of generator in list comprehension
To get the desired result, the "inner" generator would have to be run as many times as the "outer" generator yields a value.
But, after the first run, the "inner" generator is exhausted and cannot be run again.
Adding a print
illustrates this (simplifying the example):
>>> def inc(a, b):
... for i in range(a, b):
... print(i)
... yield i
...
>>> a = inc(1, 4)
>>> b = inc(4, 7)
>>> [(i, j) for i in a for j in b]
1 # <-- a begins to run
4 # <-- b begins to run
5
6 # <-- b exhausted here
2 # <-- a continued, but not resulting in list item, because lacking value from b
3
[(1, 4), (1, 5), (1, 6)]
The reason why not storing the generators in variables works as expected is because a new "inner" generator is created for each iteration of the "outer" generator. Again, illustrated by some prints:
>>> def inc(a, b):
... print('started', a, b)
... for i in range(a, b):
... yield i
...
>>> [(i, j) for i in inc(1, 4) for j in inc(4, 7)]
started 1 4
started 4 7
started 4 7
started 4 7
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
The reason why using range
objects or lists works as expected is because they can be iterated over arbitrarily many times without being exhausted.