Since python 2.5 there is the ability to send()
, throw()
, close()
into a generator. Inside the defined generator one can 'catch' the sent data by doing something like:
def gen():
while True:
x = (yield)
if x == 3:
print('received 3!!')
break
else:
yield x
What i am trying to play with is doing something like:
def gen2():
while True:
yield (yield)
Noticed that it is a legal generator which does something.. First thing i'm trying to figure out is:
Is there a good usage for such writing?
Also when doing something like:
g = gen2()
next(g)
g.send(10) # output: 10
g.send(2) # output: nothing
g.send(3) # output: 3
g.send(44) # output: nothing
Why each second 'send' does not do anything?
yield (yield)
first yields None
from the inner yield
. It then receives a value from send
or next
. The inner yield
evaluates to this received value, and the outer yield
promptly yields that value.
Each yield
conceptually has two parts:
send
or next
.send
or next
call.Similarly, each send
or next
conceptually has two parts:
yield
expression that the generator is currently paused at. (This value is None
for next
.)yield
expression.The most confusing part of the system is probably that these parts are staggered. The two parts of a yield
correspond to two different invocations of send
or next
, and the two parts of a send
or next
correspond to two different yield
s.
If we work through a simple example:
def gen():
print('Not ran at first')
yield (yield)
g = gen() # Step 1
print(next(g)) # Step 2
print(g.send(1)) # Step 3
g.send(2) # Step 4
Here's how things work out:
Inside the generator Outside the generator
Step 1
g calls gen()
g returns a generator object
without executing the print
just yet statement.
>>> g
<generator object gen at 0x7efe286d54f8>
Step 2
next(g) sends None to g
g receives None, ignores it
(since it is paused at the start
of the function)
g prints ('not ran at first')
g executes the "transmit" phase
of the inner yield, transmitting
None
next(g) receives None
Step 3
g.send(1) sends 1 to g
g executes the "receive" phase
of the inner yield, receiving 1
g executes the "transmit" phase
of the outer yield, transmitting 1
g.send(1) receives 1 from g
Step 4
g.send(2) sends 2 to g
g executes the "receive" phase
of the outer yield, receiving 2
g reaches the end of gen and raises
a StopIteration
g.send(2) raises the StopIteration
from g