5

I'm making a Python irc bot. For some reason the yield statement in my join() method makes it skip the method altogether, but if I replace it with a return it works fine. However, I need to yield an error per each unsuccessful join attempt.

I have a join method of the bot that returns a server error command code response if the join is unsuccessful for some reason. It's None if the bot joins successfully.

unsuccessful = bot.join(channels) 

I would be able to do:

if unsuccessful: for error in unsuccessful: print(error) 

The join method looks like this

def join(self, channels): chan_errors = range(471, 480) # See RFC for commands 471-479 if isinstance(channels, str): channels = [channels,] for channel in channels: self.send('JOIN %s' % channel) for response in self.get_response('JOIN', chan_errors): # Verify if response.command in chan_errors: channels.remove(channel) yield response self.channels.append(channels) 

If I switch the "yield response" with "return response" it runs the method.

The get_response method looks like

def get_response(self, commands, terminators=None): for msg in self.msg_gen(): self.handle(msg) if msg.command in commands: if terminators is None: return msg yield msg if msg.command in terminators: return msg 

It receives messages from a message generator. The commands are the server command the caller is looking for and the terminators drop out of the generator when one is found. It's sort of like a coroutine.

Does anyone know what is happening here?

5
  • 4
    Do you understand what yield is and how to use generators? bot.join(channels) returns a generator; you need to iterate over that to get the values that it yields. Commented Oct 16, 2013 at 20:50
  • @BrenBarn I'll edit the question to illustrate that I will be using 'unsuccessful' as a generator Commented Oct 16, 2013 at 20:52
  • 1
    In what way is it "not working"? What does it do? Are you saying it doesn't print anything in the loop? Commented Oct 16, 2013 at 20:56
  • It does not step into the join() method at all and runs the very next method call. No exceptions or anything. Commented Oct 16, 2013 at 20:58
  • 5
    It won't step into the join method until you begin to iterate over unsuccessful. That's how generators work. Commented Oct 16, 2013 at 20:58

1 Answer 1

5

By putting a yield statement in the join method, you're making it a generator. Calling a generator method just creates a generator function; you need to iterate through it to get data. However, since execution inside a generator stops every time it hits a yield statement, you need to exhaust all of its contents if you want it to fully run.

This way your code runs all the way through:

join_gen = bot.join(channels) for error_msg in join_gen: print error_msg 

Generators in the Python 2 docs

I'm not sure a generator is a good option for you here though. You might be better served returning response right when you have the first error, or if you want to go through all the channels, append each error response to an error list and return that list when you're done.

Sign up to request clarification or add additional context in comments.

2 Comments

Okay, I was going to go with the latter if I couldn't get this to work anyway. Otherwise I'd be pointlessly iterating through None. It'd be like going back to Java or other languages and having to return an array. I'm surprised I couldn't find this elsewhere. I figured the generator adds to the variable as it iterates. This makes sense now.
The link Mark Hildreth posted, The Python yield keyword explained, is one of the best (if not THE best!) resources for Python generators - much better than the pydocs! e-satis just kills it :D

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.