Python Loop Challenges: 3. Delete Starting Even Numbers - list modification interrupts loop iteration

Hello, I’ve been working on challenge as in topic and encountered confusing problem:
Here is my code with debugs inserted:

def delete_starting_evens(lst):
  lst_updated = lst
  print("lst: ", lst) #debug1
  print("lst_updated: ", lst_updated) #debug2
  for x in lst:
    print("x to check: ", x) #debug3
    if (x % 2 == 0):
       print(x, " is even") #debug4
       lst_updated.pop(0)
    else:
      return(lst_updated)
  return lst_updated

print(delete_starting_evens([4, 8, 10]))

Eventually I’ve replaced line 9 with :
lst_updated = lst_updated[1:]
and it works fine.

My question is: why does for loop skip one of the ‘lst’ values (8) during the iteration?
Any modification of ‘lst_updated’ (even inside the loop) should have zero impact on iteration process imo.
When you comment lst_updated.pop(0) line iteration proceeds as expected.

*** edit:
the problem originates in my attempt to create a list copy:

 lst_updated = lst.copy()

solves all the problems, however there has to be some difference between how lst_updated.pop(0) and lst_updated = lst_updated[1:] work.

Hey there and welcome to the forums!! :slightly_smiling_face:

As you discovered it has to do with your copy. In Python any assignment done involves a pointer to the object in question. Let’s say you create a list named a, and it is stored at position 5 (for simplicity). If you assign a new variable b to equal a, b = a than b points to that same spot in memory:

    position_5
     /     \ 
   a         b

If you were using say a (integer/string/tuple) this wouldn’t cause any weird behavior because these are non mutable (they can’t be changed), any action on b will create a new variable and not effect a. However something like a list can be mutated, so an action on b changes a.

>>> a = [1, 2, 3]
>>> b = a
>>> b.pop()
>>> print(a)
[1, 2]

So what’s different between b.pop(0) and b = b[1:]?
We’re using an assignment in the latter, yet not it the former. While the .pop() method modifies the original object, a string slice returns a new one. We don’t actually modify the original, we make a copy following certain parameters and return that.


Now as you saw we can preserve the original list by making a full copy of it using lst.copy() or list(lst), however making a copy like this is not the most efficient. Suppose we have a list that’s thousands of numbers long and only the last number is odd? Copying the original and then modifying the rest might not be the best idea. Instead we could loop read through the list, and when we find the first odd, return a slice of that point through the rest of the list.

Up to you if you want to look at one way or not.
def foo1(lst):
  for i in range(len(lst)):
    if (lst[i] % 2):
      return lst[i:]
  return []
3 Likes

Hello. I am just staring learning Phyton. Can anybody help me to understand why the does not work. What did I miss?

def delete_starting_evens(lst):
for num in lst:
while num % 2 == 0:
lst.remove(num)
if num % 2 == 1:
break
return lst

print(delete_starting_evens([4, 8, 10, 11, 12, 15]))

Hello @codeslayer51797, welcome to the forums!

The problem here occurs because you’re using a while loop inside a for loop. The problem is that the first iteration of your for loop sets num equal to the first element of the list. Then, your nested while loop will continue to remove items from the list indefinitely (if num is even).

This happens because the while loop condition is checking if num is even, and since num stays the same (assuming the first element of the list is even, and therefore num is even), your while loop will continue looping until the remove() function throws an error saying that there’s no instance of num in lst.


As an aside, it’s considered bad practise to modify the list you’re iterating through, as it can lead to some very weird behaviour.