If we replace the code at the end of the frange() function as shown in the following code snippet, we will turn the function into a generator. Generators do not have return statements; instead, they have yield statements. If a generator runs out of values, that is, if control reaches the end of the function, instead of returning, Python automatically raises a Stoplteration exception:
# Build and return a list # Return each value on demand result =  while start < (stop - (inc / 2.0)):
while start < (stop - (inc / 2.0)): yield start result.append(start) start += inc start += inc return result
Now, if we call frange(5), we will get back a generator object, not a list. We can force the generator to give us a list by doing this: list(frange(5)). But a more common use of generators is in loops:
This will output "0.01.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0" whichever version we use. But for long lists the generator version will be much more efficient, because rather than creating the whole list in memory like the list version, it creates only one item at a time.
The yield statement behaves like a return statement, but for one crucial difference: After yield has returned a value, when the generator is next called it will continue from the statement following the yield with all its previous state intact. So the first time the frange() generator is called, assuming, say frange(5), it returns 0.0; the second time it returns 1.0, and so on. After returning 9.0 the while expression evaluates to False and the function terminates.
★Mutable parameters in Python are similar to Pascal's var parameters and to C++'s non-const references.
Because the function is a generator (and this is the case purely because we have used yield), when it finishes it does not return a value, but instead raises a StopIteration exception. In the context of a for loop, the for gracefully handles this particular exception, taking it not as an error, but as an indication that the iteration has completed, so the for loop ends and the flow of control moves to the for loop's else suite, or to the statement following the for loop's suite, if there is no else. Similarly, if we coerce a generator into a list, the list constructor will automatically handle the StopIteration exception.
A generator is an object that has a next() function, so we can explore the behavior of our frange() generator interactively if we wish:
>>> list(frange(1, 3, 0.75)) [1, 1.75, 2.5]
>>> gen = frange(1, 3, 0.75) >>> gen.next() 1
Traceback (most recent call last): File <pyshell#126>, line 1, in -toplevel-gen.next() StopIteration
We generated the whole three-item list using list(), and then we used the generator returned by frange() to produce each successive value in the same way that a for loop does.
Was this article helpful?