Consider this Python snippet:
range1 = range(3)
range2 = range(2)
print([(e1, e2) for e1 in range1 for e2 in range2])
This displays 3x2 = 6 tuples, as expected:
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
Fine! Now, let's try this slight variant:
iter1 = iter(range(3))
iter2 = iter(range(2))
print([(e1, e2) for e1 in iter1 for e2 in iter2])
The output is surprisingly different:
[(0, 0), (0, 1)]
This result looks wrong or, at the least, inconsistent. This behaviour occurs at least on Python 2.7, 3.12 and 3.14.
Notes:
- There are many variants for highlighting the issue, including defining iterators by generators (
yieldstatement). The present description is just the simplest form I've found to highlight the misbehaviour in my code. - From my understanding, the root issue is that outer
iter1stops as soon as inneriter2stops, which is unexpected. - The problem does not appear when the iterator objects are instantiated "inline". For example,
[(e1, e2) for e1 in iter(range(3)) for e2 in iter(range(2))]returns the 6 expected tuples.
Before opening a Python issue ticket, I'd like to have some feedback on this odd phenomenon, which I consider as a plain bug.
iter2while dealing with the first item initer1.iter(range(2))again for each item initer1, so whether or not each one gets exhausted is irrelevant.