I am confused about why different looping constructs behave so differently when used with a simple generator. Consider the following code:
example_list = [1, 2, 3, 4, 5] for_count = 0 next_count = 0 while_count = 0 def consumer(iterable): for item in iterable: yield return for item in consumer(example_list): print("For Count: {0}".format(for_count)) for_count += 1 # First while loop while consumer(example_list).next(): print("Next Count: {0}".format(next_count)) next_count += 1 # Second while loop while consumer(example_list): print("While Count: {0}".format(while_count)) while_count += 1 if while_count > 10: # add contrived limit for infinite loop break The output for this code is:
For Count: 0
For Count: 1
For Count: 2
For Count: 3
For Count: 4
While Count: 0
While Count: 1
While Count: 2
While Count: 3
While Count: 4
While Count: 5
While Count: 6
While Count: 7
While Count: 8
While Count: 9
While Count: 10
I would appreciate help understanding the following:
- How does the
forloop know when to end but not the secondwhileloop? I expected bothwhileloops to exit immediately sinceNoneis yielded. - Why doesn't using
.next()raise an exception? Theconsumerroutine isn't a class with a__next__()method defined, so does it magically appear when you use theyieldkeyword? - Why is it that if I change
consumertoyield item, the firstwhileloop become infinite like the second one? - If I change
consumerto simply return instead of yielding anything, the secondwhileloop exits immediate instead of becoming infinite. I've been under the impression that ayieldis essentially areturnthat you can resume from. Why are they treated differently by awhileloop?