Why doesn't iterating with `for` work while removing items from a list?

Hmmm…Great catch! Thank you! But why isn’t it running? When there are no items left in the list, the ‘if’ statement is not satisfied and neither is the ‘elif’ below it (they both require more than 0 items in the list to execute). That leaves the ‘else’ and the expected “IndexError: pop from empty list” exception but that did not happen. Why?

Your code works on all the significant tests…

>>> def delete_starting_evens(lst):
	for x in lst:
		if x % 2 == 0 and len(lst) > 0:
			lst = lst[1:]
		elif x % 2 != 0 and len(lst) > 0:
			return lst
		else:
			lst.pop(0)
	return lst

>>> f = delete_starting_evens
>>> f([2, 4, 6, 8, 10])
[]
>>> f([])
[]
>>> f([2, 3, 4, 6, 8, 10, 11])
[3, 4, 6, 8, 10, 11]
>>> 

I’ve not removed the else, but am about to.

>>> def delete_starting_evens(lst):
	for x in lst:
		if x % 2 == 0 and len(lst) > 0:
			lst = lst[1:]
		elif x % 2 != 0 and len(lst) > 0:
			return lst
	return lst

>>> f = delete_starting_evens
>>> f([2, 4, 6, 8, 10])
[]
>>> f([])
[]
>>> f([2, 3, 4, 6, 8, 10, 11])
[3, 4, 6, 8, 10, 11]
>>> 

what is the problem? If there are no elements in the list, the loop won’t run, so it won’t reach the else clause? Else is nested within the for loop.

1 Like

Your explanation is very clear. I understand that the ‘else’ clause is not running. I was just curious as to why I didn’t get the IndexError exception as a result of that unnecessary ‘else’ clause. Is there a reason the code skipped over the ‘else’ instead of sending me the error?

The for loop ended, perhaps?

1 Like

The else is completely unnecessary, and could never possibly be executed. If there is a value for x it will either be even or odd. As @stetim94 said, once there are no values left for x, the loop is finished.

1 Like

Got obsessed with this exercise and how many different beautiful and ugly solutions here could be. Tried to find the best one. My definitions of “the best”:

  • it should be compact for readability
  • it should not modify the list for performance
  • it should pass tests: [all evens], [evens and at least one odd], [empty], [0], [0,0,0].

Strangely enough, if I’d try to follow the task exactly, there would be luck of beauty:

def delete_starting_evens3(lst):
  for _ in range(len(lst)):
    if not lst[0] % 2:
      del lst[0] #yeah, modifying the original
    else:
      break
  return lst

Try again

def delete_starting_evens0(lst):
  for i in range(len(lst)):
    if lst[0] % 2 == 1:
      return lst
    else:
      del lst[0] # not youuuuuu again!
  return lst

again

def delete_starting_evens4(lst):
  for i in range(len(lst)):
    if not lst:
      return []  # here will exit all evens list, without this check wil throw IndexError, coz we didn't remove lst[0] 
    elif lst[0] % 2 == 1: #looking for the first odd number
      return lst #here will exit 1st odd list
    else:
      lst = lst[1:] #keeep looking with reduced list
  return lst  # here will exit [] or [0] any other crap if any

Got proof for those who prefer using a list comprehension everywhere: list comprh. doesn’t work in this case, because it never stops (or I don’t know the way to stop it.) I call it X-rays vision! Unstoppable.

def delete_starting_evens_comprh(lst):
  return [i for i in lst if i % 2 == 1]
#[11, 15]
#[]

again?

def func5(a):
  """try...exept: will take care of "list index out of range" when [all evens]"""
  try:
    while a[0] % 2 == 0: #checking for evens
	    a = a[1:] #am I modifying the list with reassigment? no
    else:
      return a # found 1st odd, exiting with last modified list
  except IndexError: 
    return []

I’m admiring the thinking out of a box and tool usage, hence “the most elegant solution” (according to ME!) goes to mr.@mtf

 def delete_starting_evens_mtf(lst):
	for i, x in enumerate(lst):
		if x % 2: 
			return lst[i:]
	return []

Such a simple task uncovered some unexpected knowledge. That could have just escaped if we’d get asked to remove from the end of the list. Thanks, everyone for reading pleasure.

2 Likes

I like how you considered different approaches, and consider the pros and cons of each approach. Such an important aspect of programming. Writing elegant, maintainable code and being able to refactor are so important

I think you have a very good attitude and way of thinking about programming. I am very impressed, good job :slight_smile:

1 Like

I would have to echo @stetim94’s sentiment… Way to make good use of your time studying variations to learn their inner workings, benefits and advantages. You raise some good points.

2 Likes