The yield from grammar in Python 3
- 2020-05-24 05:47:38
- OfStack
preface
Recently, I have been playing with Autobahn. It gives an example based on asyncio. Failed.
pip install asyncio
When I reported invalid syntax directly, I thought there was something wrong with the processing of to3 -- I can't blame you, since package was written with 2 and then converted to 3 -- I found out that asyncio only supported version 3.3+. Then I went back to the code and found a sentence
yield from
;
yield
I know, but
yield from
What is?
PEP-380
Okay, so I came up with the title google,
yield from
All lives and all lives are in this PEP, but the general idea is original
yield
The statement only gives control of CPU back to the direct caller. If you want to refactor the logic of an generator or coroutine with yield statement into another generator, it will be very troublesome, because the generator on the outside will be responsible for sending messages to the generator on the inside. So someone had an idea for python to encapsulate messaging and make it transparent to the programming ape, and there it was
yield from
.
Specifies the PEP - 380
yield from
The semantics, or the nested generator should have the behavior pattern.
Suppose that the A function has a statement like this
yield from B()
B()
If it returns an iterable (iterable) object, b, then A() will return an generator -- a, according to our naming convention -- then:
__next__()
Method, otherwise the send method of b is called. If an StopIteration exception is generated on the method call to b, a continues execution
yield from
The following statement, while other exceptions will be propagated to a, leading to the execution of a
yield from
Throws an exception when the.
If an exception other than GeneratorExit is passed from throw to a, the exception is passed directly from throw to b. If b's throw method throws StopIteration, a continues to execute; Other exceptions cause a to throw exceptions as well.
If an GeneratorExit exception is called by throw into a, or if a's close method is called, and b also has close methods, b's close method is also called. If this method of b throws an exception, it causes a to throw an exception as well. Conversely, if b succeeds and close drops, a will also throw an exception, but a specific GeneratorExit exception.
In the a
yield from
The expression evaluates to the first parameter of the StopIteration exception thrown at the end of the b iteration.
In the b
return <expr>
The statement actually throws it
StopIteration(<expr>)
Exception, so the value of return in b becomes a
yield from
The return value of an expression.
Why are there so many requirements? Because the behavior of something like generator becomes very complex with the addition of the throw method, especially in the case of several generator in 1, it requires a process-managing-like meta-language to operate on it. All the above requirements are to unify the complicated behavior of 1 generator, so it is not easy.
I admit I didn't see what the authors of PEP were trying to say, so it might help to "refactor" it once.
A useless example
It doesn't work because you probably don't really want to write a program like this, but... Anyway, that's enough.
Imagine an 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 into the local variable total, and stops iterating when the BreakOut exception is received. As for the other SwitchSign exception, it should be easy to understand, but I won't reveal the plot here.
From the code point of view, by
inner()
The resulting generator receives data for operations through send, while the throw method accepts control from external code to execute different branches of code, so far so clear.
And then because there's a change in demand, we need to
inner()
Before and after this code is added the initialization and cleanup code. Since I thought "don't touch the code until it's broken," I decided to let it
inner()
I'm going to keep the status quo, and I'm going to write another one
outer()
, and put the added code in
outer()
And provide with
inner()
1 type of operation interface. Due to the
inner()
It takes advantage of several features of generator, so
outer()
Here are five things you must do:
outer()
1 generator must be generated;
In each iteration of step 1,
outer()
To help
inner()
Returns the iteration value;
In each iteration of step 1,
outer()
To help
inner()
Receiving data sent from outside;
In each iteration of step 1,
outer()
To deal with
inner()
Receive and throw all exceptions;
in
outer()
By close,
inner()
Also be properly close dropped.
According to the above requirements, in a world with only yield,
outer()
It might look something 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 ratio
inner()
It's even longer, and it hasn't handled the close operation yet.
Now let's try alien technology:
def outer2():
print("Before inner(), I do this.")
yield from inner()
print("After inner(), I do that.")
In addition to meeting all of the above requirements, these four lines of code save some paper when printed.
We can
outer1()
and
outer2()
It is not difficult to find that the behavior of the two generator is basically 1. In this case, alien technology is of course the first choice in most cases.
Questions about generator and coroutine
I have seen coroutine under Python before and I think it is strange. I can see their behavior pattern clearly, but I don't know why I should use this pattern. generator and coroutine have one kind of external interface. What puzzles me most is that coroutine under Python ties the two operations of "messaging" and "scheduling" to one yield -- even if it does
yield from
I don't see the need to do that. The concept of coroutine would have been easier to understand if 1 had separated the two semantics syntactically from the beginning and designed a set of interfaces for generator and coroutine respectively.
conclusion
The above is the whole content of this article, I hope the content of this article can bring 1 definite help for everyone to study or use python, if you have any questions, you can leave a message to communicate.