Generators in The Event Coordinator Project - Problem with .send()

Hey there, the context of this is that we are presented with a function, we should turn it into a generator function and print out the first 10 names on the guestlist object generated by the function. This part of the problem is easy and was solved by me, in many different ways - since I was trying to solve the actual problem at hand which I will present to you now:

The second part of the set asks to send a guest into the generator and return it in its yield (the guest info is to be presented as “Jane,35”)

I have written this code:

guests = {}
def read_guestlist(file_name):
  text_file = open(file_name,'r')
  while True:
    input_guest = yield 
    if input_guest != None:
      line_data = input_guest.split(",")
      # print(line_data)
    else:
      line_data = text_file.readline().strip().split(",")
    if len(line_data) < 2:
    # If no more lines, close file
      text_file.close()
      break
    name = line_data[0]
    age = int(line_data[1])
    guests[name] = age
    yield "{} {}".format(name, age)


guest_list = read_guestlist("guest_list.txt")

count = 0
for guest in guest_list:
  if count < 20:
    print(guest)
    count += 1
  elif count == 20:
    guest_list.send("Jane,35")
    print(guest)
  else:
    break

# print(len(guests.keys()))
# print(guests.keys())

The guest is added to the guest dict, as you see from my bottom two lines which have been commented out, I was trying to bug fix the problem which is that “Jnae, 35” doesn’t come out as a yield when I print out the entire iteration or when I print next for the next few lines, even when I reach StopIteration … no Jane.

The “name” variable becomes “Jane”
The “age” variable becomes “35”
They are added to the dict
The object when iterated through fully, after the .send() results:

None
Tim 22
None
Tonya 45
None
Mary 12
None
Ann 32
None
Beth 20
None
Sam 5
None
Manny 76
None
Kenton 15
None
Kenny 27
None
Dixie 46
None
Mallory 32
None
Julian 4
None
Edward 71
None
Rose 65
None

Please help me figure out how to yield “Jane 35” and why it’s not done through my logic. thank you so much in advance - by the way I read many posts about this problem - no solution yet.

You have two yield statements which is what’s giving you the awkward None values every other iteration. I can’t see a way you’d make the two work out easily.

Additionally one of the yields is never assigned. I’ve not run through the logic but I’d hazard a guess that you’re sending data to that non-assigned statement and that would be why you never see it (it’s just discarded).

I’ve always found that task a really weird way to try and introduce users to sending items into a generator and a lot of folks seem to get stuck on it. I’d highly suggest using something like Python Tutor - Visualize Python, Java, JavaScript, C, C++, Ruby code execution to visualise the execution of this frame which makes .send and generators much less mysterious.

1 Like

I’ve had many versions of my code, some without the first yield directed ‘nowhere’ and the same issue persisted, even when I had only the yield at the bottom; I’ve tried other peoples code, anyone who claimed to solve it, the same problem persists; the value pushed through the .send() method, the “Jane,35” text won’t be printed out.

For example, this was my first code to solve this problem:

guests = {}
def read_guestlist(file_name):
  text_file = open(file_name,'r')
  while True:
    line_data = ''
    input_guest = yield line_data 
    if input_guest != None:
      line_data = input_guest.split(",")
    else:
      line_data = text_file.readline().strip().split(",")
    if len(line_data) < 2:
      text_file.close()
      break
    name = line_data[0]
    age = int(line_data[1])
    guests[name] = age
    yield "{} {}".format(name, age)


guest_list = read_guestlist("guest_list.txt")

for guest in guest_list:
  print(guest)

guest_list.send("Jane,35")
print(next(guest_list))

This gives me the following:

Tim 22

Tonya 45

Mary 12

Ann 32

Beth 20

Sam 5

Manny 76

Kenton 15

Kenny 27

Dixie 46

Mallory 32

Julian 4

Edward 71

Rose 65

Traceback (most recent call last):
  File "script.py", line 25, in <module>
    guest_list.send("Jane,35")
StopIteration

The tool you sent me is freaking awesome, I love it, gonna book mark it; sadly, however, it cannot use “open” and won’t be as useful for this problem … tho I could write tho code to iterate through something other than a file and keep the same logic to see what could happen visually.

1 Like

If you can’t use open, import io and io.StringIO operate like a text buffer and could get you the same behaviour. As a very basic example that may need some alteration to match you original data…

import io buffer = io.StringIO("""\ Tom, 35 Manny, 76\ """ ) buffer.seek(0) for line in buffer: print(line.strip().split(','))

But yeah, I think you’re going to want to drop the two yields as it can be hard to keep track of normally, unnecessary output is bad practice and harder to debug. A second issue I’ll note is that the .send method itself returns something. So when you use send you’ll want to do something with its return, e.g. next_item = genobj.send(data); print(next_item).

So far as I can tell the instructions, although rather odd expect you to only print out the first 10 guest, then try to squeeze “Jane” into the mix and then to print the other guests. A for loop with the style you have makes that a little more difficult (consider using a range, consider using enumerate or use the counter you have but you must catch the output of .send).

2 Likes

I see, they did shoot the error message when I coded the same logic, Import io. I will do that and see :smiley:
For your suggestion of a different type of for loop, I saw people using a for in range loop to no avail, but I will try it and see if that changes anything. I would love to learn from you tho :smiley: on why this type of for loop makes it difficult in this scenario :thinking: , why is it easier to use a for in range loop?

Many many thanks, appreciate your replies.

1 Like

Hmm, perhaps running it on your own device would be better.

The reason I mention the loop as a potential issue is that it iterates through each element in turn, it doesn’t immediately allow for items to be squeezed in partway through. That’s not to say you can’t do it but you’ll have to add in logic to handle a case like that. In particular the bit about .send returning a value means that the iteration is no longer as simple as it might have been.

A range indexed loop might be more difficult in another way since there’s no the length of items you need to go through changes (you’re adding bits in so it’s dynamic, not static).

While and a condition might be more robust but it’s not the only option. Playing around with it a bit might be the best possible choice (at least you’re learning/experiment then :slightly_smiling_face:).

1 Like

That makes a lot of sense, actually. I will try that. I think I understood the difference you are talking about between the two types of loops in this problem set.

Thank you so much <3 appreciate you.

1 Like