Intro
Coroutines are computer program components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing more familiar program components such as cooperative tasks, exceptions, event loop, iterators, infinite lists and pipes.
Coroutines are a natural way of expressing many algorithms, such as simulations, games, asynchronous I/O, and other forms of event-driven programming or co-operative multitasking. Python’s generator functions are almost coroutines — but not quite — in that they allow pausing execution to produce a value, but do not provide for values or exceptions to be passed in when execution resumes. They also do not allow execution to be paused within the “try” portion of try/finally blocks, and therefore make it difficult for an aborted coroutine to clean up after itself.
Such applications could then write co-routines to do non-blocking socket I/O by yielding control to an I/O scheduler until data has been sent or becomes available.
Because generator-iterators begin execution at the top of the generator’s function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs. Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression.
As with the next() method, the send() method returns the next value yielded by the generator-iterator, or raises SopIteration if the generator exits normally, or has already exited. If the generator raises an uncaught exception, it is propagated to send()’s caller.
迭代器(Iterators)
Python supports a concept of iteration over containers. This is implemented using two distinct methods; these are used to allow user-defined classes to support iteration. Sequences, described below in more detail, always support the iteration methods.
Iteration is a process implying iterables (implementing the __iter__()
method) and iterators (implementing the __next__()
method). Iterables are any objects you can get an iterator from. Iterators are objects that let you iterate on iterables.
The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:
iterator.__iter__()
Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements. This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.
iterator.next()
Return the next item from the container. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.
1 | #!/usr/bin/env python |
下面是搬自stackoverflow的一个cheat sheet。
1 | sequence |
生成器(Generators)
生成器提供了一种出色的方法,使得需要返回一系列元素的函数所需的代码更加简单、高效。基于yield指令,可以暂停一个函数并返回中间结果。该函数将保存执行环境并且可以在必要时恢复。
1 | #!/usr/bin/env python |
In Python, everything described here as a coroutine is still a generator.
Remember, yield both passes a value to whoever called next(), and saves the “state” of the generator function.
Generators vs. Iterators
A generator function is slightly different than an object that supports iteration
A generator is a one-time operation. You can iterate over the generated data once, but if you want to do it again, you have to call the generator function again.
This is different than a list(which you can iterate over as many times as you want)
When you’re using send to “start” a generator, you must send None.
- Generators are used to generate a series of values
- Yield is like the return of generator functions
- The only other thing yield does is save the “state” of a generator function
- A generator is just a special type of iterator
- Like iterators, we can get the next value from a generator using next() for gets values by calling next() implicitly.
Generator function vs Generator expression
Generator functions
They are coded as normal def but use yield to return results one at a time, suspending and resuming.
Generator expressions
There are similar to the list comprehensions. But the return an object that produces results on demand instead of building a result list.
Because neither of them constructs a result list all at once, they save memory space and allow computation time to be split by implementing the iteration protocol.
Python引入的与生成器相关的最后一个特性是提供了与next方法调用的代码进行交互的功能。yield将变成一个表达式,而一个值可以通过名为send的新方法来传递。
1 | def psychologist(): |
send的工作机制与next一样,但是yield将变成能够返回传入的值。因而,这个函数可以根据客户端代码来改变其行为。同时,还添加了throw和close两个函数,以完成该行为。它们将向生成器抛出一个错误:
throw 允许客户端代码传入要抛出的任何类型的异常;
close的工作方式是相同的,但是将会抛出一个特定的异常——GeneratorExit,在这种情况下,生成器函数必须再次抛出GeneratorExit或StopIteration异常。
1 | def my_generator(): |
Yield
Yield返回一个生成器。简单地讲,yield的作用就是把一个函数变成一个generator。一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
为了精通yield,你必须要理解:当你调用这个函数的时候,函数内部的代码并不立马执行。
yield produces a value, but suspends the function. function resumes on next call to next().
Generators as Pipelines
1 | import time |
1 | def grep(pattern,lines): |
1 | def grep(pattern): |
Coroutine
If you use yield more generally, you get a coroutine.
These do more than just generate values
Instead, functions can consume values send to it
1 | >>> g = grep("python") |
1 | # coroutine.py |
- Generators produce data for iteration
- Coroutines are consumers of data
- To keep your brain from exploding, you don’t mix the two concepts together
- Coroutines are not related to iteration
Reference
http://www.dabeaz.com/coroutines/
https://www.python.org/dev/peps/pep-0342/
https://docs.python.org/3/library/asyncio-task.html
https://stackoverflow.com/questions/9884132/what-exactly-are-iterator-iterable-and-iteration
https://stackoverflow.com/questions/1995418/python-generator-expression-vs-yield