In Python 3.3, generator added a syntax yield from, what is the yield from? What is the grammar? The following through this article mainly give you a detailed introduction of the Python 3 yield from the syntax of the relevant data, the need for friends can refer to the following to see together.
Objective
Recently in tinkering with Autobahn, it has given an example is based on Asyncio, thinking that put on the pypy3 run to see unexpectedly on ... Have failed. pip install asyncio
directly reported invalid syntax, coarse see also thought 2to3 deal with the time there is a problem--this can not blame me, good ~ Many package are written with 2 and then turned into 3--the results found that Asyncio originally only support the version of 3.3+, and then look back to the code, impressively found a sentence yield from
yield
I know, but yield from
it's God's horse?
PEP-380
Okay, this headline is my google out of the yield from
past life in this pep inside, in short, the original yield
statement can only return the CPU control to the direct caller, when you want to be a generator or coroutine with The logical refactoring of the yield statement to another generator (the original is subgenerator) can be very troublesome, because the outside generator is responsible for message delivery for the generator inside; So someone has an idea to have Python deliver the message. Wrap it up so that it's transparent to the program ape, so it's there yield from
.
PEP-380 Specifies yield from
the semantics, or nested generator, of the behavior patterns that should exist.
Let's say that there is a statement in a function
Yield from B ()
B()
Returned is an iterative (iterable) object B, then a () will return a generator--according to our naming convention, named a--so:
Each value produced by the B iteration is passed directly to the caller of a.
All values sent to a by the Send method are passed directly to B. If the value sent is None, the method of B is called __next__()
, otherwise the Send method of B is called. If a stopiteration exception is generated for a method call to B, a will continue to execute the yield from
subsequent statement, while the other exception will propagate to a, causing a to yield from
throw an exception at execution time.
If an exception except Generatorexit is thrown into a, the exception is thrown directly into B. If the throw method of B throws Stopiteration, a will continue to execute, and the other exception will cause A to also throw an exception.
If a Generatorexit exception is thrown into a, or the Close method of a is called, and B has a close method, the Close method of B is also called. If this method of B throws an exception, it causes A to also throw an exception. Conversely, if B succeeds close off, a also throws an exception, but it is a specific generatorexit exception.
The evaluation result of an expression in a yield from
is the first parameter of the stopiteration exception thrown at the end of Iteration B.
The statement in B return <expr>
actually throws an StopIteration(<expr>)
exception, so the value returned in B will be the return value of the expression in a yield from
.
So many demands for God's horse? Because the behavior of generator this kind of thing becomes very complicated after adding the throw method, especially in the case of several generator together, it needs to be manipulated by a meta-language similar to the process management. All of the above requirements are to unify the generator originally complex behavior, natural simple not to come.
I admit I didn't see what THE PEP writers were trying to say, so the "refactoring" would probably help a little bit.
A useless example.
It's useless because you probably don't really want to write the program like this, but ... It's enough to explain the problem anyway.
Imagine having such a generator function:
def inner (): Coef = 1 total = 0 in while true:try: input_val = yield of all all equals = Total + Coef * Input_val except S Witchsign: coef =-(COEF) except BreakOut: return Total
The generator generated by this function accumulates the value received from the Send method into the local variable total, and stops iterating when the breakout exception is received; the other switchsign exception should not be difficult to understand, it is not a spoiler.
From the code point of view, the inner()
generator obtained by the function receives data for the operation via send, while the throw method takes control of the external code to execute different branches of code, so far it is clear.
Next, because of changes in requirements, we need to inner()
add code to initialize and clean up the field before and after this code. Since I think "no bad code does not move", I decided to let the inner()
status quo, then write another outer()
, put the added code in outer()
, and provide the inner()
same interface with the operation. Due to the inner()
use of several features of the generator, outer()
there are five things that must be done:
outer()
A generator must be generated;
In each step of the iteration, outer()
to help inner()
return the iteration value;
In each step of the iteration, outer()
to help inner()
receive data sent externally;
In each iteration of the step, the outer()
inner()
receiving and throwing of all exceptions are handled;
outer()
when it is close, inner()
it is also to be properly close off.
According to the above requirements, in a world of yield only, outer()
it may be long:
Def outer1 (): Print ("before inner (), I do the") I_gen = inner () Input_val = None Ret_val = i_gen.send (input_val) while T Rue: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 Does that. ")
WTF, this code is inner()
longer than itself, and the close operation has not been processed.
Now let's try alien technology:
Def outer2 (): Print ("before inner (), I do the") yield from inner () print ("After inner (), I-do.")
In addition to fully complying with the above requirements, the four lines of code when printed out can also save a bit of paper.
We can outer1()
test the data on and outer2()
off separately, and the passing of the exception, it is not difficult to find that the behavior of the two generator is basically consistent. That being the case, alien technology is of course preferred in most cases.
Questions for generator and coroutine
From previous contact with Python under the coroutine it feels strange, I can see their behavior patterns, but do not understand why to use this mode, generator and coroutine have the same external interface, is generator created coroutine, or coroutine created generator? The best thing I can think of is that the coroutine under Python binds both "messaging" and "dispatch" to a yield--even with yield from
--I don't see the need to do that. If the two semantics are separated from the syntactic level at the outset, and a separate set of interfaces are designed for generator and coroutine, the concept of coroutine will probably be easier to understand.