In python3.3, generator adds the syntax yieldfrom. what is the role of yieldfrom? What is the syntax? The following article describes in detail the yieldfrom syntax in Python3. For more information, see. In python 3.3, generator adds a syntax yield from. what is the role of yield from? What is the syntax? The following article describes in detail the yield from syntax in Python 3. For more information, see the following.
Preface
Recently, I am playing the drums Autobahn. It provides an example based on asyncio. I want to say that I can run it on pypy3 ...... Failed.pip install asyncioI directly reported invalid syntax, and thought there was a problem when 2to3 was handled.-this cannot blame me, okay ~ Multiple packages are written in 2 and converted to 3. the result shows that asyncio only supports version 3.3 + and then looks back at the code again.yield from;yieldI know,yield fromIs it Shenma?
PEP 380
Okay, this title is from google,yield fromThe past and present are all in this PEP.yieldThe statement can only return the CPU control to the direct caller. when you want to reconstruct the logic of a generator or coroutine containing the yield statement into another generator (the original version is subgenerator), it will be very troublesome, because the external generator is responsible for sending messages to the generator, someone has the idea that python encapsulates the message transmission so that it is transparent to the program.yield from.
PEP-380 specifiesyield fromOr the behavior pattern that the nested generator should have.
Assume that function A has such A statement.
yield from B()
B()If the returned result is an iterable object B, A () will return a generator -- according to our naming convention, named A -- so:
Each value generated by iteration B is directly transmitted to the caller of.
All values sent to a through the send method are directly passed to B. If the sent value is None, B's__next__()Otherwise, call the send method of B. If a StopIteration exception occurs when calling Method B, a will continue to executeyield fromAnd other exceptions will be transmitted to a, causing a to executeyield fromThrow an exception.
If an exception except GeneratorExit is throw to a, the exception is throw to B. If the throw method of B throws StopIteration, a continues to execute. if other exceptions occur, a also throws an exception.
If a GeneratorExit exception is throw to a, or the close method of a is called, and B also has the close method, the close method of B is also called. If this method of B throws an exception, a also throws an exception. Otherwise, if B closes successfully, a will throw an exception, but it is a specific GeneratorExit exception.
A mediumyield fromThe expression evaluate is the first parameter of the StopIteration exception thrown at the end of iteration B.
In Breturn The statement will actually throwStopIteration( ) Exception, so the return value in B will become inyield fromThe return value of the expression.
Why are there so many requirements for Shenma? This is because the actions of generator become very complex after the throw method is added. in particular, when several generators are together, you need to operate them in a metaracter similar to process management. All the requirements above are to unify the complex behaviors of the generator, which is naturally not easy.
I admit that I didn't understand what the PEP author was trying to say, so it would be helpful to "refactor" it again.
A useless example
It's useless because you probably don't really want to write the program like this, ...... It is enough to explain the problem.
Suppose there is a generator function:
def inner(): coef = 1 total = 0 while True: try: input_val = yield total total = total + coef * input_val except SwitchSign: coef = -(coef) except BreakOut: return total
The generator generated by this function accumulates the values received from the send method to the local variable total, and stops iteration when the BreakOut exception is received. it is not difficult to understand another SwitchSign exception, this is not a spoiler.
From the code point of viewinner()The generator obtained by the function receives the data used for calculation through sending, and accepts the control of external code through throw to execute different code branches. it is clear so far.
Next, we needinner()Before and after the code, initialize and clean up the code on site. Since I think "don't change without bad code", I decidedinner()Maintain the status quo, and then write anotherouter(), Put the added code inouter()And provideinner()The same operation interface. Becauseinner()Using several features of generatorouter()These five things must also be done:
outer()A generator must be generated;
In each iteration,outer()To helpinner()Return the iteration value;
In each iteration,outer()To helpinner()Receive externally sent data;
In each iteration,outer()To processinner()Receives and throws all exceptions;
Inouter()When it is closed,inner()It should also be properly closed.
According to the above requirements, in the only yield world,outer()It may be long like this:
def outer1(): print("Before inner(), I do this.") i_gen = inner() input_val = None ret_val = i_gen.send(input_val) while True: try: input_val = yield ret_val ret_val = i_gen.send(input_val) except StopIteration: break except Exception as err: try: ret_val = i_gen.throw(err) except StopIteration: break print("After inner(), I do that.")
WTF, this code ratioinner()It takes a long time, and the close operation has not been processed.
Now let's try out alien technologies:
def outer2(): print("Before inner(), I do this.") yield from inner() print("After inner(), I do that.")
In addition to fully complying with the above requirements, the four lines of code can save some time for printing.
We canouter1()Andouter2()Test data and abnormal transmission on the two nodes, it is not difficult to find that the actions of the two generators are basically the same. In this case, alien technology is of course the preferred choice in most cases.
Questions about generator and coroutine
Coroutine in Python is strange from the past. I can see their behavior patterns, but I don't understand why I want to use this pattern. generator and coroutine have the same external interface, is generator creating coroutine or coroutine creating generator? What bothers me most is that coroutine in Python binds the "message passing" and "scheduling" operations to a yield-even ifyield fromThis situation has not changed-I cannot see the necessity of doing so. If the two semantics are separated from the syntax at the beginning and a set of interfaces are designed for generator and coroutine respectively, the concept of coroutine is probably easier to understand.
For more details about the yield from syntax in Python 3, refer to the PHP Chinese website!