Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python generator send: don't yield a new value after a send

This is kind of a weird question so I'll explain:

I have a generator like this that is acting as a generator frontend to an IRC server:

def irc_iter(): # not the real code, simplified
    msgs = get_msgs()
    for msg in msgs:
        if is_ping(msg):
            pong()
        else:
            to_send = yield msg
            for s in to_send:
                send(s)

This, theoretically, should allow me to do something cool, like:

server = connect()
for line in server:
       if should_respond(line):
           server.send('WOW SUCH MESSAGE')

However, there's a hitch: generator.send yields the next value as well. This means that server.send is also giving me the next message... which I would prefer to handle like all the other messages, yielded as line.

I know I can fix this in an ugly way, by just yielding a junk value after receiving a send, but I'm trying to keep my code elegant and that is the opposite. Is there a way to just tell the generator I don't want a new value yet?

Thanks.

like image 994
vgel Avatar asked Feb 25 '14 23:02

vgel


People also ask

What does the send () method do to a generator?

The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

Does yield stop execution Python?

Inside a program, when you call a function that has a yield statement, as soon as a yield is encountered, the execution of the function stops and returns an object of the generator to the function caller.

How do you return and yield in Python?

The yield statement hauls the function and returns back the value to the function caller and restart from where it is left off. The yield statement can be called multiple times. While the return statement ends the execution of the function and returns the value back to the caller.


2 Answers

I also just came across this issue and also didn't find anything better than dummy yield...

So in your generator code:

        # Input
        input_value = yield
        if input_value is None:
            # Handle situation if someone used your generator in a wrong way
            sys.exit(-1)
        yield "DUMMY YIELD JUST STOP HERE!!!"

And in the client code:

while True:
    values = next(my_generator)
    ...
    if values == 'INPUT_NEEDED':
        # At this point you realize it's input time
        next(my_generator) # Rewind to the input point
        my_generator.send(12345) # Returns "DUMMY YIELD JUST STOP HERE!!!"
        continue
    # Continue the main loop
like image 87
The Godfather Avatar answered Sep 27 '22 19:09

The Godfather


It looks like the problem comes from you calling the generator twice per iteration, once with .send(None) (in the for loop) and once with .send(response).

It would be a simple fix if the for loop could iterate over .send() instead of .next(), but I am not familiar with any way to get that to work (optional extended continue statement in pep342?) without wrapping it in another generator (possibly using a queue to push values into .send() on .next() calls). The simplest solution, though, would probably be:

server = connect()
response = None 
try:
    while True:
        line = server.send(response)
        response = None
        if should_respond(line):
            response ='WOW SUCH MESSAGE'
except StopIteration:
    pass
like image 38
bj0 Avatar answered Sep 27 '22 18:09

bj0