The following paragraph is an exact quote from the book
Writing iterables and iterators from scratch can be a bit tedious, so Python provides generators to simplify the creation of iterable sequences. A generator results from calling a function containing a yield statement. Executing such a function does not invoke the function's body, but rather returns a generator object. Here's a generator that produces successive powers of two, up to some limit:
def powers_of_two(limit):
value = 1
while value < limit:
yield value
value += value
# Use the generator
for i in powers_of_two(70):
print(i)
1
2
4
8
16
32
64
The following 2 line function accomplishes the exact same thing without using a generator and is certainly not "a bit tedious" as the author suggests.
def alternative_powers_of_two(limit):
for i in range(int(math.log(limit,2)+1)):
print(2**i)
So what is the value of this generator stuff? Rather than simplifying writing iterators, it seems to do the exact opposite -- a least in the example the author uses.