<div dir="ltr"><div>Hi,</div><div dir="ltr"><br></div><div dir="ltr">I often use generators when I want to bake some smarts into my producers in a typical producer and consumer pattern. Twice in the past year or so on different projects after a few iterations on my producer/consumer code ("iterations", ha!) I would eventually find I wanted my consumer to send some simple feedback to the producer - when using generators send works great for this!</div>
<div dir="ltr"><div><br></div><div>I normally start here:</div><div><br></div><div>q = producer()<br></div><div><br></div><div><div>def consume(q):</div><div> for item in q:</div><div> success = handle(item)</div>
<div><br></div><div>But then ended up here:</div><div><br></div><div>def consume(q):</div><div> success = None</div><div> while True:<br></div><div> try:</div><div> item = q.send(success)</div><div>
except StopIteration:</div><div> break</div><div> success = handle(item)<br></div><div><br></div><div>But this always feels clunky; almost "out of order" - I've normally got some logging and other bits in there. I'm just trying to iterate over the queue, the StopIteration just feels like noise.</div>
<div><br></div><div>It seems I always started out with a simple iteration and then end up refactoring in the send function once I realize I need to add something extra into my producer:</div><div><br></div><div>I want to just say:</div>
<div><br></div><div>def consume(q):</div><div> for item in q:</div><div> success = handle(item)</div><div> q.send(success) # oh how I wish I could just add this here!</div><div><br></div><div>But I can just "tack on" the send because it consumes too, so I have to rephrase the "simple" iteration into a "while True try except StopIteration break" - yuk.</div>
<div><br></div><div>I normally just end up adding a little helper function, which I've named "sending" and use it like this:</div><div><br></div><div>def consume(q):</div><div> for item in sending(q, lambda: success):</div>
<div> success = handle(item)</div><div><br></div><div>^ much closer to the original iterative code!</div><div><br></div><div>Here's the implementation if you didn't already guess:</div><div><br></div><div><div>
# this function is missing from stdlib for some reason...</div><div>def sending(g, sender):</div><div> """</div><div> Iterate over g with send</div><div><br></div><div> :params g: the iterable</div>
<div> :params sender: the callable returning the value to send</div><div> """</div><div> yield g.next()</div><div> while True:</div><div> yield g.send(sender())</div></div><div><br></div>
<div>This will raise StopIteration as soon as g is exhausted, but since the caller is already consuming "sending" with a for loop it all works ok in the end.</div><div><br></div><div>Does anyone else have a different way of iterating over a generator while sending values? Does everyone just already have a similar function stuck in their bag? Does this happen to other folks frequently enough that a generalizable solution could be added to stdlib? Maybe it's too simple to be worth anyone's time or i'm missing something that already works better...</div>
<div><br></div><div>Thanks!</div><div><br></div><div>-clayg</div></div><div><br></div></div></div>