Try a concrete example?
t1 = (1,2,3) t2 = ("a", "b", "c") t3 = ("first", "second", "third") (x,y,z for x in t1 for y in t2 for z in t3) Do What I Mean is the crossproduct, 27 elements long, which you get with a list comprehension. This will happen if at least (all but one) generators are pre-evaluated. It will also happen if at least (all but one) generators are restarted after they raise StopIterator. Doing strictly what happens today gets only (1,'a',"first"), (1,'a',"second"), (1,'a',"third") because t3 has raised StopIteration by the time t1 or t2 advance. Effectively, only the last iterator matters. Keeping the last value of an iterator also adds (1, 'b', "third"), (1, 'c', "third"), (2, 'c', "third"), (3, '3', "third") for seven values -- but perhaps not a well-chosen seven. Pre-evaluating only the leftmost gets all combinations of (1,2,3) and ("first", "second", "third"), but still misses items like (2, 'b', "second"). Since these are in the middle of the series, they are the most likely to be forgotten during testing. Restarting the second (and later) iterators for each value of the first iterator does the right thing, without requiring any magical pre-evaluation. (Unless the iterator can't be restarted -- in which case there is no right thing, and the user should have decided what to cache.) Thus my suggestion of (x,y,z for x in t1 for y in t2 for z in t3) <==> for x in t1.__iter__(): for y in t2.__iter__(): for z in t3.__iter__(): yield x,y,z For the example data, this creates 3 separate iterators on the letters and nine iterators on the words - but produces the expected result, without having to store or pre-evaluate anything. -jJ