Today we’ll see how to send objects to generators.
If you’re not quite sure what generators are and how they work, you definitely should read one of my previous articles where I’m doing my best to explain it.
And now, back to our topic.
The generator object can send a message object to the generator using the send method. The send method sends an object to the generator but at the same time it returns the value yielded by the generator. Here’s an example. Let’s define our generator:
>>> def letter_generator(text):
... position = 0
... while True:
... message = yield text[position]
... if message!= None:
... position = message
... else:
... position += 1
...
This generator yields the letters of text. The local variable position must be assigned a value before it can be used. We assign the iterator returned by the generator to the letters variable:
>>> letters = letter_generator("abcdefghijklmnopqrstucwxyz")
Now we can start the generator and let it yield the characters in the text passed as an argument to the generator function. Before the generator object can send an object to the generator, the generator must be started. That’s why we need to use a next call before the first send call. Otherwise, we would get an error. So, let’s start the generator:
>>> next(letters)
'a'
Now, the generator yielded the first letter and stopped. It yielded the first letter because the value of the variable position is 0. If it were set to 3, it would yield ‘d’ instead. So, after yielding text[0], the generator stopped and is going to wait until we call next again. Actually, we have two options now: We can start sending a message object to the generator using the send method, or make it yield the next character by using the next method. Let’s try the latter:
>>> next(letters)
'b'
Now the generator function resumed at the point it had left off before, so after the yield statement. The value of the variable position is still remembered, it’s 0. The next method actually also sends an object to the generator, which is None. As the object sent by next is None, the position variable is incremented by 1, and text[1] is yielded, which is ‘b’. The generator stops again and is waiting for the next message, which can be either None from next or another value from send. The object sent by next or send is assigned to a variable in the generator, here called message.
Now, let’s see what happens if the generator object, which is the letters iterator, sends an object using the send method. The object sent is ‘7’.
>>> letters.send(7)
'h'
As this time the message is not None, the position variable is assigned its value, which is 7. Now text[7] is yielded, which is ‘h’. The execution of the generator stops after the yield statement again. It’s waiting for another next or send message. Let’s use next this time:
>>> next(letters)
'i'
As the generator receives the None message from next, the value of the position variable is incremented by 1 and text[8] is yielded, which is ‘i’. Now let’s send the message ‘12’ using the send method:
>>> letters.send(12)
'm'
Again, as the message is not None, it’s assigned to the position variable and text[12] is yielded, which is ‘m’. Let’s try next this time:
>>> next(letters)
'n'
And now let’s use send(7):
>>> letters.send(7)
'h'
This time text[7] is yielded. One last time let’s call next. This time text[8] is yielded:
>>> next(letters)
'i'
And we could go on and on like this.
Here’s the video version of the article: