Reputation: 320
While I try to understand the mechanism of generator's send() method, I encountered a strange behaviour in my example. (VS Code Python 3.10)
def MultipleOfThree():
num = 1
while True:
if num % 3 == 0:
yield num
num += 1
#test
iter = MultipleOfThree()
for m3 in iter:
print(m3)
This code is working as expected and prints
>>> 3,6,9,12,15,18,21.....
When I add the send() statement in for loop, and arrange the MultipleOfThree() func like below
def MultipleOfThree():
num = 1
while True:
if num % 3 == 0:
i = yield num
if i is not None:
num = i
num += 1
#test
iter = MultipleOfThree()
for m3 in iter:
print(m3)
iter.send(m3) #for simplicity send the m3 itself
it prints
>>> 3,9,15,21,27
I couldn't understand the mechanism of send() here, why escapes the for loop. I study the subject from this page How to Use Generators and yield in Python.
Upvotes: 2
Views: 140
Reputation: 17332
The generator makes a stop at each yield
of some value. It continues after the value is consumed by calling next
on the corresponding iterator. It continues until the next yield
(or generator exit).
A for
loop uses this iterator protocol to consume the yielded values one at a time and runs the loop body with each one.
A send
is similar to next
. It consumes a value, makes the generator to advance, but in addition to next
It also sends a value to the generator. The sent value becomes the value returned by the yield
(in the generator).
Your code is interacting with the generator at two places: in the for
statement and in the send
statement. But you are printing only one of the values, that's why you see only every second.
Add a print
and you'll see all values again:
for m3 in iter:
print(m3) # print 1 (yielded by for loop)
print(iter.send(m3)) # print 2 (yielded by send)
Upvotes: 1
Reputation: 26981
First initialize the generator:
iter = MultipleOfThree()
Get the first value:
m3= next(iter)
Then continue in a while loop:
while True:
print(m3)
m3 = iter.send(m3)
You wish to get the first value using next(iter)
or iter.send(None)
in order to reach the first multiple of three, without any input.
Then, you want to send back the input and continue from there using iter.send(m3)
.
Overall code:
def MultipleOfThree():
num = 1
while True:
if num % 3 == 0:
i = yield num
if i is not None:
num = i
num += 1
iter = MultipleOfThree()
m3= next(iter)
while True:
print(m3)
m3 = iter.send(m3)
Upvotes: 0