This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 ! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment