What is a generator (StackOverflow)
next()
on, such that for every call it returns some value, until it raises a StopIteration
exception, signaling that all values have been generated. Such an object is called an iterator.yield
anywhere in a function makes it a generator. Summary:
yield
instead of return
next()
In [1]:
def gencubes(n):
for num in range(n):
yield num**3
for x in gencubes(10):
print x
In [5]:
# the above is actually *functionally* the same as
def gencubes_2(n):
out = []
for num in range(n):
out.append(num**3)
return out
for x in gencubes_2(10):
print x
# however, in gencubes_2, a list is created and stored in memory, whereas
# gencubes is maintaining its state and returning one result at a time
# without holding it all in memory
In [9]:
def genfibon(n):
a = 1
b = 1
for i in range(n):
yield a
temp = a
a = b
b = temp + b
for i in genfibon(10):
print i
In [13]:
def fibon(n):
a = 1
b = 1
output = []
for i in range(n):
output.append(a)
a,b = b, a+b
return output
fibon(10)
Out[13]:
In [17]:
def simple_gen():
for x in range(3):
yield x
g = simple_gen()
print next(g)
print next(g)
print next(g)
# calling this next() a 4th time throws an error for StopIteration
# because there's nothing left to yield. A for-in loop prevents this
# by stopping when there is nothing left to iterate over
print next(g)
In [18]:
# being an iterator is not the same as being iterable
# for example, a string is iterable, but not an iterator:
s = 'hello'
for let in s:
print let
In [19]:
# Running this line prompts the error:
# TypeError: str object is not an iterator
next(s)
In [22]:
s_iter = iter(s)
print next(s_iter)
print next(s_iter)
print next(s_iter)
print next(s_iter)
print next(s_iter)
# one more call will throw a StopIteration error
In [25]:
def mygen(n):
yield n
yield n+1
g = mygen(6)
print next(g)
print next(g)
print next(g) # error
In [39]:
# generator expressions: used to have shorthand for the most common gens
g = (n for n in range(3,6))
print next(g)
print next(g)
# note their similarity to list comprehensions
h = [n for n in range(3, 6)]
print h
Observe that a generator object is generated once, but its code is not run all at once. Only calls to next actually execute (part of) the code. Execution of the code in a generator stops once a yield statement has been reached, upon which it returns a value. The next call to next then causes execution to continue in the state in which the generator was left after the last yield. This is a fundamental difference with regular functions: those always start execution at the "top" and discard their state upon returning a value.
In [ ]: