https://docs.python.org/3/reference/expressions.html#yieldexpr
https://rmariano.eu/posts/exploring-generators-and-coroutines/
Exploring Generators and Coroutines
PEP-255 (Simple Generators)
[yield is a statement; lazy evaluation]
PEP-342 (Coroutines via Enhanced Generators)
[yield is an expression; send(), throw(), close()]
PEP-380 (Syntax for delegating to a Sub-Generator)
[return in generators; yield from]
PEP-525 (Asynchronous Generators)
https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
How the heck does async/await work in Python 3.5? [long essey]
Use cases of generators:
(a) Working with data streams or large files, like CSV files.
(b) Generating an infinite sequence.
This is an expression that returns an iterator.
# sum of squares 0, 1, 4, ... 81 alist = [i*i for i in range(10)] # list comprehension gen = (i*i for i in range(10)) # generator expression result = sum(i*i for i in range(10))
This is a function which returns a generator iterator. It looks like a normal function except that it contains yield expressions (instead of return) for producing a series of values.
def iter_squares(n):
for i in range(n):
yield i*i
result = sum(iter_squares(10))
def my_range(stop):
"""My version of range(stop)."""
value = 0
while value < stop:
yield value # yield instead of return
value = value + 1
# Example 1 - using for loop (hidden iteration protocole).
for i in my_range(10):
print(i)
# Example 2 - explicit iteration protocole.
gen = my_range(3)
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # StopIteration, gen is exhausted
print(next(gen)) # StopIteration
def iter_squares2(): # an infinite iterator
i = 0
while True:
yield i*i
i += 1
def fibonacci(): # an infinite iterator
"""The Fibonacci sequence."""
a, b = 0, 1
yield a
yield b
while True:
a, b = b, a + b
yield b
# Example 1 - using for loop (hidden iteration protocole).
for i in fibonacci():
print(i)
if i > 100: # we have to break the loop manually
break
# 0 1 1 2 3 5 8 13 21 34 55 89 144
# Example 2 - the generator is alive!
fib = fibonacci() # fib is a generator object
for i in fib:
print(i)
if i > 100:
break
print(next(fib)) # 233
print(next(fib)) # 377
List comprehensions can be faster to evaluate than the equivalent generator expression.
import sys import timeit sqr_list = [i*i for i in range(1000000)] print(sys.getsizeof(sqr_list)) # 8697464 bytes sqr_gen = (i*i for i in range(1000000)) print(sys.getsizeof(sqr_gen)) # 120 bytes t1 = timeit.Timer(lambda: sum(sqr_list)) print(t1.timeit(1)) # 0.00961924200237263 t2 = timeit.Timer(lambda: sum(sqr_gen)) print(t2.timeit(1)) # 0.05263053500675596 (5 times slower)
https://realpython.com/introduction-to-python-generators/
# Problem: couting rows in a text file.
txt_gen = txt_reader("some_file.txt")
line_count = 0
for line in txt_gen:
line_count += 1
def txt_reader(file_name): # possible MemoryError
file = open(file_name, "r")
result = file.read() # loads everything into memory at once
result = result.rstrip("\n") # remove last "\n" if present
result = result.split("\n") # divide to lines
return result # list of strings without "\n"
def txt_reader(file_name): # a generator function
for line in open(file_name, "r"):
yield line.rstrip("\n") # lines without "\n"