https://www.geeksforgeeks.org/use-yield-keyword-instead-return-keyword-python/
Yield will suspend the execution of a function, sending a value back to the caller, but retain enough state to enable the function to resume where it left off.
Use yield when we want to iterate over a sequence without returning all of it at once (and having to store it all in memory). The normal return keyword would just give us everything and the function would be done.
Generators produce data - they are functions that contain the yield keyword automatically become generators.
The generator function returns a generator object which can be iterated over:
Previously, generators had to be completely consumed by the function calling them and couldn't call another generator which would halt the execution of both. However, there is now the yield from syntax which can be used to yield the result from another generator.
In [1]:
def generator1():
yield 1
yield 2
yield 3
for value in generator1():
print(value)
In [2]:
def generator2():
yield "Hello"
yield "World"
my_gen = generator2()
print(next(my_gen))
print(next(my_gen))
Similar to threads, but a coroutine decides when to switch context based on the code written. Threads would switch based on the operating system (or runtime environment). Coroutines are coopertive and multitask based on how the programmer has set suspend and resume points in the code.
Similar to generators:
With a modified yield statement, data can be sent to a coroutine:
In [3]:
def check_for_value(num):
try:
while True:
received_value = (yield) #data sent to this co is stored here
if num == received_value: #check the value that was sent
print("Correct!")
else:
print("Incorrect")
except GeneratorExit: #catches coroutine.close()
print("Closing Coroutine.")
In [4]:
coroutine = check_for_value(42) #instantiating the coroutine w/42
In [5]:
coroutine.__next__() #starts co and pauses at 1st yield
In [6]:
coroutine.send(7)
In [7]:
coroutine.send(42)
In [8]:
coroutine.close() #close coroutine
Coroutines can be chained together to form a pipeline, starting with a producer as a simple function, going into a middle coroutine, and ending with an output coroutine. Data gets passed from one stage to another.
In [9]:
def producer(data_in, next_coroutine):
tokens = data_in.split(" ")
for token in tokens:
next_coroutine.send(token)
next_coroutine.close()
In [10]:
def middle_coroutine(filter_str, next_coroutine):
print("Search for words containing: ", filter_str)
try:
while True:
token = (yield)
if filter_str in token:
next_coroutine.send(token)
except GeneratorExit:
print("Done filtering")
next_coroutine.close()
In [11]:
def output_coroutine():
try:
while True:
token = (yield)
print(token)
except GeneratorExit:
print("Done printing")
In [12]:
sink = output_coroutine()
sink.__next__()
In [13]:
middle = middle_coroutine("t", sink)
middle.__next__()
input_str = "This is a sentence with some words containing the letter t."
producer(input_str, middle)
Asynchronous programming is where execution order is not 100% known ahead of time. Some parts of the code may execute before other parts, with no way to guarantee the order.
Programming construct that waits for events to happen and dispatches responses to them. The asyncio library was added to python to provide an event loop.
Reference: https://hackernoon.com/asynchronous-python-45df84b82434
Use the decorator "@asyncio.coroutine" on the generator (function) and it will now be a coroutine meant for use with asyncio and its event loop (it can also call another generator)
In [14]:
import asyncio
# Borrowed from http://curio.readthedocs.org/en/latest/tutorial.html.
@asyncio.coroutine
def countdown(number, n):
while n > 0:
print('T-minus', n, '({})'.format(number))
yield from asyncio.sleep(1)
n -= 1
asyncio.set_event_loop(asyncio.new_event_loop())
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(countdown("A", 2)), asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
async def defines a function as asynchronous (replaces @asyncio.coroutine)
await keyword replaces yield from and is there to wait for a coroutine to finish
In [15]:
import asyncio
# Borrowed from http://curio.readthedocs.org/en/latest/tutorial.html.
async def countdown(number, n):
while n > 0:
print('T-minus', n, '({})'.format(number))
await asyncio.sleep(1)
n -= 1
asyncio.set_event_loop(asyncio.new_event_loop())
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(countdown("A", 2)), asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()