Reputation: 13
I'm using yield from
, but I don't know about the influence of while
for yield
. If I put yield from
in a while
loop, it works well, but when I cancel the loop at the mean time an exception occurs.
final_result = {}
def sales_sum(pro_name):
total = 0
nums = []
while True:
x = yield
print(pro_name+" Sales volume: ", x)
if not x:
break
total += x
nums.append(x)
return total, nums
def middle(key):
while True:
final_result[key] = yield from sales_sum(key)
def middle2(key):
final_result[key] = yield from sales_sum(key)
def main(fun):
data_sets = { "A": [1200, 1500], "B": [28,55,98]}
for key, data_set in data_sets.items():
m = fun(key)
m.send(None)
for value in data_set:
m.send(value)
m.send(None)
if __name__ == '__main__':
main(middle) # work well
main(middle2) # StopIteration
I expect main(middle2)
to work well as main(middle)
, but there is a StopIteration
exception.
Upvotes: 1
Views: 748
Reputation: 104712
The cause of the unexpected StopIteration
exception in main
is that your m.send(None)
call causes your middle2
generator to be exhausted (after the sub-generator sales_sum
breaks out of its loop in response to the falsey value it received). When a generator is exhausted, it raises StopIteration
. Normally that's invisible because you consume iterators in for
loops, but in this case, it breaks your code.
There are a few ways you could fix this. One would be to use a two-argument call to next
instead of using m.send(None)
:
next(m, None)
This does the same thing as m.send(None)
, but has the added benefit of suppressing the StopIteration
. Note that the None
in the call to next
is not really the same as the one in send
. It's the default return value in the case of an exhausted iterator, not the value that gets sent in (which is always None
when using next
).
Another approach would be to change middle2
so that it doesn't end when the sales_sum
generator does. You could add an extra yield
statement at the end, so that it yields control one last time after doing its assignment to final_result
when its sub-generator returns.
A final idea would be to replace m.send(None)
with m.close()
. This would require some changes in final_result
, as the close
call will throw a GeneratorExit
exception into the generator. If you expect it, you could use that as your signal to be done instead of looking for a falsey value:
def sales_sum(pro_name):
total = 0
nums = []
while True:
try:
x = yield
except GeneratorExit:
return total, nums
print(pro_name+" Sales volume: ", x)
total += x
nums.append(x)
With this change, middle2
would not need any modification.
Upvotes: 1
Reputation: 531165
sales_sum
is a finite iterator. middle2
iterates over it exactly once; middle
tries to iterate over it multiple times.
Upvotes: 1