1

Everyone, I wrote this simple program (to learn python) that prints a list of messages and then fills a new list of printed message. It's very easy code , but something goes wrong with for loop.

The code is :

def Print_It (message):
        print (message)
       
def Send_mex (To_send):
        for sende in To_send:
            Print_It(sende)
            current_message = List_of_Message.pop()
            Sended_message.append(current_message)

List_of_Message = ['GO!!!', 'WAIT!!!', 'DAMN!!', 'OUCH!!!']
Sended_message = []

Send_mex (List_of_Message)
Print_It (List_of_Message)
Print_It (Sended_message)

The Output is:

GO!!!
WAIT!!!
['GO!!!', 'WAIT!!!']
['OUCH!!!', 'DAMN!!']

The expected output is:

GO!!!
WAIT!!!
OUCH!!!
DAMN!!!
[]
['OUCH!!!', 'DAMN!!','GO!!!', 'WAIT!!!']

for some reasons that I don't understand the for loop works only for two arguments of list not for all...

Any suggestions?

Peter Leimbigler
  • 10,775
  • 1
  • 23
  • 37
Kko_Viv
  • 27
  • 5
  • 1
    You should not modify a list as you are looping over it. In your function `List_of_Message` and `To_send` are the _same_ list. – Mark Jul 04 '23 at 15:15
  • 1
    You are both passing the list to the subroutine _and_ referencing the global variable from within the subroutine. That doesn't make sense and will lead to conflicts. Don't use global vars, but if you must, at least don't modify global vars from within a subroutine. Also, never loop over a list that you are modifying. If you are going to pop things off the list, just use a `while len(...)` loop instead of a `for` loop. – Mark Reed Jul 04 '23 at 15:16
  • OK... I understand the problem now... For the moment I change: Send_mex (List_of_Message) to Send_mex (List_of_Message[:]) and it's works... Thanks!!! Now try to use a while len (...) instead for loops – Kko_Viv Jul 04 '23 at 15:20
  • Does this answer your question? [How to remove items from a list while iterating?](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating) – Daraan Jul 04 '23 at 15:24

5 Answers5

1

You're passing the list of messages to send into send_mex, so it doesn't need to then reach out and modify list_of_message using its global name. It should only modify what's given to it. (Having it modify its parameter is also not great, but better than having it go change a global variable directly.)

That goes for the global sent-message queue, too; I'd just have it return the sent messages. The calling code can append them to the global if it ever calls send_mex more than once.

A for loop will break if you modify the list while you're looping over it, so don't do that either. If you're popping items off the list, just use a while that loops while the list has nonzero length:

def send_mex(to_send):
    sent = []
    while to_send:
        message = to_send.pop()
        print_it(message)
        sent.append(message)
    return sent


list_of_message = ['GO!!!', 'WAIT!!!', 'DAMN!!', 'OUCH!!!']
sent_message = send_mex(list_of_message)

If you append your final output code, you'll see that list_of_messages winds up empty, and the items that started out in it are now in sent_message.

You'll notice I uncapitalized the names; in standard Python coding style, initial capital letters are reserved for constants, not variables and functions.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
1

You're messing with the list List_of_messages: you're both iterating on it and popping items from it. This is something you should definitely avoid; it generates unexpected behavior like the one you're experiencing. The same list on which you iterate gets changes, so that the iteration cycles come out different from what you imagined.

I suggest the following solution:

def print_message(message):
    print(message)

def send_messages(to_send, sent):
    to_send.reverse()
    while to_send:
        message = to_send.pop()
        print_message(message)
        sent.append(message)

messages = ['GO!!!', 'WAIT!!!', 'DAMN!!', 'OUCH!!!']
sent = []

send_messages(messages, sent)
print('List of messages to send:')
print_message(messages)
print('List of sent messages:')
print_message(sent)

Here you first reverse the list (which, in any case, will be emptied). Then you use a while cycle where at each iteration you check if the list is not empty (to_send will evaluate to a boolean False in the while only when it will be empty). Then you pop the message (remember that the list was reversed), you print it and you append it to the sent ones.

EM90
  • 552
  • 1
  • 8
  • 22
0

I can see you are a beginner, so I will try to make you work a little bit.

Try this script and try to understand it. I just added print so you can see what is going on with your List_of_Message and Sended_message.

def Print_It(message):
    print(message)


def Send_mex(To_send):
    for sende in To_send:
        Print_It(sende)
        print(f"Content of List_of_Message before pop : {List_of_Message}")
        current_message = List_of_Message.pop()
        print(f"Content of List_of_Message after pop : {List_of_Message}")
        print(f"Content of Sended_message before append : {Sended_message}")
        Sended_message.append(current_message)
        print(f"Content of Sended_message after append : {Sended_message}")

        print("\n\n")


List_of_Message = ["GO!!!", "WAIT!!!", "DAMN!!", "OUCH!!!"]
Sended_message = []

Send_mex(List_of_Message)
Print_It(List_of_Message)
Print_It(Sended_message)

I think you have understand that that pop() will remove the item from the list.

BUT

"The argument passed to the method is optional. If not passed, the default index -1 is passed as an argument (index of the last item)."

So if you don't give an argument, it will remove the last items of the list.

Feel free to reach back if you still have issue or you still don't understand why your for loop is finish early. I will edit to add more info if needed.

RaiZy_Style
  • 103
  • 10
0

Try this (EDITED2MEET your desired output) We use 2 for loops!!!:

def Print_It (message):
    print (message)
       
def Send_mex (To_send):
    for sende in To_send:
            Print_It(sende)
    for sende in range(len(To_send)):
        Sended_message.append(List_of_Message.pop())
                    
List_of_Message = ['GO!!!', 'WAIT!!!', 'DAMN!!', 'OUCH!!!']
Sended_message = []

Send_mex (List_of_Message)
Print_It (List_of_Message)
Print_It (Sended_message)

The result..:

GO!!!
WAIT!!!
DAMN!!
OUCH!!!
[]
['OUCH!!!', 'DAMN!!', 'WAIT!!!', 'GO!!!']
Harry
  • 3,592
  • 5
  • 21
  • 16
-2

In general, you don't want to modify variables outside the function if they are also passed through the function. In this case, since List_of_Message is passed through as To_send, To_send is a shallow copy of List_of_Message, meaning if you modify List_of_Message, To_send is changed as well.

In this case, since you are modifying To_send as you are iterating over it, an easy fix would be to copy it at the start of the for loop.

def Print_It (message):
    print (message)
       
def Send_mex (To_send):
    for sende in To_send.copy():
        Print_It(sende)
        current_message = List_of_Message.pop()
        Sended_message.append(current_message)

List_of_Message = ['GO!!!', 'WAIT!!!', 'DAMN!!', 'OUCH!!!']
Sended_message = []

Send_mex (List_of_Message)
Print_It (List_of_Message)
Print_It (Sended_message)
  • `To_send` is _not_ a copy or `List_of_Message`, shallow or otherwise. It is a reference to the same list. – Mark Jul 04 '23 at 15:27
  • It's looping through a list and then popping a different list that happens to be the same but isn't necessarily... – Mark Reed Jul 04 '23 at 15:32
  • This doesn't answer the question to "Why the for loop is stop without any reason". – RaiZy_Style Jul 04 '23 at 15:33