# hello.
def myrange(a, b):
r = []
while a < b:
r.append(a)
a += 1
return r
for x in myrange(0, 10):
print(x)
# 0, 1, 2, 3, ..., 9
# cool.
for x in myrange(0, 10000000):
if x == 10:
break
print(x)
# (a couple of seconds pass...)
# 0, 1, 2, 3, ..., 9
# not so cool.
# we just generated a list with 10 MILLION elements
# only to use the first 10 !
# this is clearly sub-optimal.
# the problem here is that myrange() executes *eagerly*
# that is, it will go ahead and generate the list of values
# independently of whether we use them or not
# well python sucks. i'm back to writing C...
# hold your horses there, cowboy !
# generator functions to the rescue !
def mysuperrange(a, b):
while a < b:
yield a
a += 1
# okay...
# wait for it...
for x in mysuperrange(0, 10000000):
if x == 10:
break
print(x)
# holy pants ! that was fast. what's going here?
# think of generator functions as `lazy lists`
# lazy? i want my code to be blazing fast !
# yes. lazy. this is a good thing.
# generator functions, as their name imply, will generate
# values *as needed*. this puts the control back to where
# it belongs: the consumer of the data
# when called, generator functions return *immediately* and
# produce generator objects, which follow the iterator pattern:
# when we call next() on this object, our function starts running.
# ...but it doesn't run all the way through.
# instead, it executes until it finds a `yield` statement.
# if a value accompanies this yield statement, it is returned
# by next() and our function stops running. yes. it. stops. running.
# pay attention...
def lazy_hello():
print('hello')
yield
print('world')
yield
gen = lazy_hello()
# ... no output ...
next(gen)
# "hello"
# woah.
next(gen)
# "world"
# okay. i see...
# ...but wait:
next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
# so it really *is* an full-blown iterator.
# yes. that's why we can use it in for-in's:
def abc():
yield 'a'
yield 'b'
yield 'c'
for letter in abc():
print(letter)
# 'a', 'b', 'c'
# this is also called `deferred` execution.
# this means execution is postponed until next() is called.
# additionally, you may think of functions using `yield` as
# functions with multiple `return` statements, except these
# return statements don't end function execution. instead,
# each value returned is accumulated in a lazy list which
# may be consumed via the iterator pattern.
# that's all folks !