Python Introducing Lists Using Lists Mutability

<noob />
<noob />
16,316 Points

questions regarding to the use of copy() and what really happen

Hi, so i had to experiment a bit to understand it better and i still have questions. lets look at this example:

list_of_letters = ["a", "b", "c", "d"]
for letter in list_of_letters:
    list_of_letters.remove(letter)
print(list_of_letters)

# we get this output -["b", "d"]

but when we iterate through a copy of list_of_letters:

list_of_letters = ["a", "b", "c", "d"]
for letter in list_of_letters.copy():
    list_of_letters.remove(letter)
print(list_of_letters)

#we get the intended  ouput: [] 

I want to understand what really happend here,if we follow the logic of the code,first we iterate through a copy of the list and then we REMOVE from the MASTER list, but my question is this: this is working because now we have a copy of the list and when later i print() the list i actually printing the MASTER LIST after we modified her?

i couldn't figured out why we dont remove from the copy list as well?

Steven Parker

KRIS NIKOLAISEN

Steven Parker
Steven Parker
174,023 Points

:bookmark: Hi, I was alerted by your tag, but I see Dave got here first and left an excellent response.

That makes a good point - there are lots of folks willing to help, so you might give the community a chance to respond to a new general knowledge question before tagging anyone specific.

10 Answers

Dave StSomeWhere
Dave StSomeWhere
19,783 Points

It is because the list's index values are mutable and are being modified in the for loop.

In your example A has an index of 0, B is 1, C is 2 and D is 3.

When you do a remove of the first letter, A, at index zero, the values change index values - resulting in B at index 0, C at index 1 and d at index 2.

Then your loop move on to index 1 (which is now C) and you remove it, skipping letter B. C is removed and D is now at index 1 and the loop move on to index 2 - which ends the loop - leaving B and D (skipping B and D).

By looping over the copy, the original index values are used and don't get messed up. Then when you do the remove method it removes by the value at that index. In copy index of 1 has a value of B and you remove the value of B at index 0 in the original list... and so on...

Try this code to assist is seeing what's happening (set the copy to a variable to see values):

list_of_letters = ["a", "b", "c", "d"]
copy_of_letters = list_of_letters.copy()
for letter in copy_of_letters:
    print("\nLetter is {}, list index is {} and copy index is {}".format(letter, list_of_letters.index(letter),
                                                                         copy_of_letters.index(letter)))
    list_of_letters.remove(letter)
print("\nfinal list of letters is --> ", list_of_letters)

#output
Letter is a, list index is 0 and copy index is 0

Letter is b, list index is 0 and copy index is 1

Letter is c, list index is 0 and copy index is 2

Letter is d, list index is 0 and copy index is 3

final list of letters is -->  []

Hopefully that helps.

<noob />
<noob />
16,316 Points

I didnt understand ur example.. my main qustion was Do i use the remove() method on the copy() of the list or do i use the remove() method on the original list()

Carlos Muñoz Sanchezllanes
Carlos Muñoz Sanchezllanes
4,065 Points

My hamster mind finally gets it! This is why I love passing over the questions after a video. To have a further analysis of what I learned and to use it in motion. evil squirrel laugh

Jenny Tang
Jenny Tang
3,650 Points

tysm, dave! your explanation was super clear! all makes sense now! :)

<noob />
<noob />
16,316 Points

Steven Parker I still didnt understand his solution. i find ur exaplnations very helpful, take it as a compliment :] my main qustion was Do i use the remove() method on the copy() of the list or do i use the remove() method on the original list()

Dave StSomeWhere
Dave StSomeWhere
19,783 Points

I also find Steven's answers very useful (and better) :smile:

I thought you were trying to understand what is happening in the loop and why we need to use a copy of the list.

Since the goal is to modify the original list.

You need to do the remove on the original list.

The copy is only used to maintain the proper indexing of the original list since the remove() method modifies the original list and will alter the behavior of the list. That is what I tried to display with the coding example.

Sorry to be so confusing. I'll be quiet now.

<noob />
<noob />
16,316 Points

Dave StSomeWhere

I run ur code, why the orginial list index is 0 evrey iteration?

Dave StSomeWhere
Dave StSomeWhere
19,783 Points

Because you just removed the letter at index 0 and the list changes and moves the next letter to index 0 - that's the key point of why you need to copy and why letters b and d are skipped in your first example.

Steven Parker
Steven Parker
174,023 Points

I'm flattered, both of you! :blush: And you will remove from the original list. The reason you want to use a copy of the list for the loop is to prevent the removals from causing other items to be skipped over. Dave was explaining how that happens.

<noob />
<noob />
16,316 Points

Dave StSomeWhere Dave thanks for the coding example!, I wanted to know what happend in the loop and on what we use the remove() method.

i only have 1 more question: in my exmaple:

list_of_letters = ["a", "b", "c", "d"]
for letter in list_of_letters:
    list_of_letters.remove(letter)
print(list_of_letters)

# we get this output -["b", "d"]

i understand why we left with ["b", "d"], but i still have doubts with the remove() part. we remove "a" first and the list is now [ "b", "c", "d"] >> b is in index 0, c is in index 1 and d is in index 2, now the remove() method removes "c" because of what?, is it because he now remove the element in index 1 because he already removed the elemnt in index 0? remove() always start removing elements from the first index if we use remove in a for loop?

if u run this exmaple

list_of_letters = ["a", "b", "c", "d"]
for letter in list_of_letters:
    list_of_letters.remove(letter)
    print("You just removed {}".format(letter))
print(list_of_letters)

i see that a is removed and then we skip b and then c is removed , so remove() after he removes an element is moving to the next index

<noob />
<noob />
16,316 Points

Steven Parker maybe u can answer my last question

Steven Parker
Steven Parker
174,023 Points

Don't be impatient, give Dave time to respond. :hourglass:

Dave StSomeWhere
Dave StSomeWhere
19,783 Points

Just give me a chance :smile:. Steven, your ability to succinctly answer questions and express concepts continues to be very helpful, informative and appreciated.

Sounds like your question is on the remove() and I think you are confusing the remove and the indexing of the loop.

The Doc for Remove state

list.remove(x) Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.

The remove is by value and not index. The loop is by index.

Does that answer your question? :tropical_drink: :palm_tree:

<noob />
<noob />
16,316 Points

Dave StSomeWhere so in my example:

list_of_letters = ["a", "b", "c", "d"]
for letter in list_of_letters:
    list_of_letters.remove(letter)
    print("You just removed {}".format(letter))
print(list_of_letters)

so what exactly happen here, if remove() removes the first item from the list, then lets say >>> "a" is removed then "b" is now the value , then why he don't remove him?(why the behavior now is that "b" got skipped? I understand what list.remove() does and i understand what happend in the loop but i dont understand what do u mean by " Remove the first item from the list whose value is equal to x.",

in this example now "b" is equal to x and he don't get removed he got skipped so its not by value, if it was by value then why it got skipped?

if i dont iterate over a copy() of list_of_letters, first we itearte on "a" and "a" got removed, then we left with ["b","c",d"] no we on the next iteration which is in index 1 then "c" got skipped that's the behavior?

Dave StSomeWhere
Dave StSomeWhere
19,783 Points

I think you are confusing the loop and the remove, and you need to keep them separate.

So, the loop will continue bumping up the index and move from letter to letter....

When looping over the copy (with your "a, b, c d") example, there are 4 items in the list with indexes from 0 to 3. By using a copy the loop sets the letter variable for each value. Then within the loop you do a remove based on the value. Issuing a remove() for the letter "a", will remove the first occurrence of the letter "a". If the list contained 3 entries with the value 'a" then the remove only removes the first one. Like - ["a", "b", "c", "d", "a", "q", "a"] - we have the value "a" at index 0, 4 and 6. remove('a') will only remove the one at index 0 (the first one, you would need to issue two more removes to get all three removed).

On your question:

if i dont iterate over a copy() of list_of_letters, first we itearte on "a" and "a" got removed, then we left with ["b","c",d"] no we on the next iteration which is in index 1 then "c" got skipped that's the behavior?

No, you mixing the loop and the remove - you need to keep them separate.

In the non-copy() example: The loop is bumping up the index.

First time through the loop - the loop starts at index 0 and updates the letter variable to "a" since that is the value at index 0. Then you issue a remove for the letter "a". The remove finds the first letter "a" at index 0 and removes it. Thus changing the list (the mutable part) and now the list contains b,c and d at index 0, 1 and 2.

Then we loop again. The index is bumped from 0 to 1 and the loop sets the letter variable to c (skipping "b" at index 0 - we're now on index 1 - this is the ah ha part). Now the letter variable is set to "c" and we do a remove (by value) on the letter "c". Changing the list again which now becomes "b" and "d" at index 0 and 1.

Then the loop bumps the index from 1 to 2 and ends because it is through the list - and the result is just b and d.

???

<noob />
<noob />
16,316 Points

Dave StSomeWhere

Thanks :D i finally understand that part so when we loop over a copy() of the list we dont lose any elements in the list because we iterate over the first item in the copy() and then we remove the value from the original list and because remove() works by value i just remove the letter at that iteration!

gabrielle moran
gabrielle moran
1,769 Points

If you run your code through step by step on this site you can see that the progress of the loop always takes it back to the next index, not the one it has already done! http://pythontutor.com