◦ next(g), will advance to the next yield statement. ◦ If there are no more elements, StopIteration is raised • The idiom “for x in g:...” follows this protocol ◦ Calls iter(g) → __iter__, __next__
n: yield i i += 1 total = sum(new_range(LIMIT)) total = 0 i = 0 while i < LIMIT: total += i i += 1 Alternative Pythonic way A generator gives a proper abstraction, with all the advantages of iterators for free (combine with itertools, chain, assign, pass along, etc.).
caller pushes data into the coroutine. ◦ yield usually appears on the RHS value = yield result • The coroutine is suspended at the yield Coroutines via Enhanced Generators
• yield from ◦ Gets all values from an iterable object ◦ Produce the values from the sub-generator ◦ Open a channel to the internal generator ◦ Can get the value returned by the internal generator
and .throw() are passed along. • Returned (yielded) values, bubble up. yield from acts as a “channel” from the original caller, to the internal generators.
scheduled to run, and updates them with .send(), next(), .throw(), etc. • The coroutine we write, should only delegate with await (yield from), to some other 3rd party generator, that will do the actual I/O. • yield , yield from, await give the control back to the scheduler.
to have a yield in a coroutine :-( ◦ “async def” only allowed “return” or “await” • “Produce elements, one at the time, asynchronously”: ◦ async for x in data_producer: ... ◦ Asynchronous iterables, were required • Difference between iterator (__iter__ / __next__), vs. a generator ◦ But for asynchronous code (__aiter__ / __anext__), vs. async generator
better patterns. • Generators and coroutines are conceptually different, however their implementation details are similar. • yield from is a construction that allows more powerful coroutines. • Any yield from chain of calls ends with a yield (at the end, there is a generator).