In [12]:
# generators are iterables that produce items on demand

In [13]:
# generators are defined by functions that use yield. the general term for this is "coroutine"

In [14]:
# yielding can be done anywhere in a function
from random import random

def gen():
    yield 1
    if random() > 0.5:
        yield 2
    yield 3
    
for i in gen():
    print i


1
3

In [15]:
# to get items from a generator, you can iterate over it
g = gen()

for a in g:
    print a


1
2
3

In [16]:
# you can also use next
g = gen()
g.next()


Out[16]:
1

In [17]:
g.next()


Out[17]:
3

In [18]:
g.next()


---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-18-d7e53364a9a7> in <module>()
----> 1 g.next()

StopIteration: 

In [19]:
# generators don't ever have to terminate
def gen():
    while True:
        yield 0
        yield 1

# this notebook does not run the following code,
# which would go into an infinite loop

g = gen()
[g.next() for i in range(10)]


Out[19]:
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

In [ ]:
# generators cannot be reset to an earlier state (e.g., their initial state)
# so you can only iterate over a generator once
# but you can call the coroutine as many times as you want