In Python, a generator is a built-in object that satisfies the iterator interface. Specialized syntax such as generator expressions and the
yield keyword makes generator objects a concise way of creating an iterator. A generator is lazy, producing an item only when asked for it, so it is memory efficient. As such, generators can also represent infinite sequences.
When a function body contains a
yield statement, the function will then output a generator object when called.
def double_iter(): curr = 1 while True: yield curr curr *= 2
Now, each time that
__next__ is invoked on the generator (e.g. through the
next function or a
for loop), code from the function will be executed up through the yield statement. The expression next to the yield statement will then be yielded as the result of invoking
__next__ on the generator object. Then, the next time
__next__ is invoked, the execution starts from where it has previously left off and continues until it hits yield again. 
>>> a = double_iter() >>> a <generator object double_iter at 0x10f14e678> >>> next(a) 1 >>> next(a) 2 >>> next(a) 4
To stop the generator from producing another value, one can either raise a
StopIteration exception or return None. Note that returning None also happens by default when one reaches the end of the body.
A generator can also be created directly via a generator expression, which uses the same syntax as a list comprehension but evaluates to a generator object instead of a list.
>>> a = (i*2 for i in range(5)) >>> a <generator object <genexpr> at 0x109d90558> >>> next(a) 0 >>> next(a) 2 >>> for elem in a: ... print(elem) 4 6 8