-4

Trying to create a function which removes odd numbers. When I run this code, it prints out only [2,3,4,5,6,7,8,9]. Is this because the return is cancelling my loop after the first iteration? If so, how can I modify this to run the loop and print out a list with all the odd numbers removed?

def evens(numbers):
    for i in range(len(numbers)):
        if numbers[i] % 2 != 0:
            del numbers[i]
        return numbers

numberlist = [1,2,3,4,5,6,7,8,9]

print evens(numberlist)

Before you all jump to downvoting me for a repeated question... I'm asking why my particular code is broken. And this has uncovered an interesting trip-up which is that using the del method in a loop means you actually need to iterate in reverse.

Dude
  • 145
  • 1
  • 9

1 Answers1

1

Indeed, you return after the first loop, change the indentation of the return statement to be one tab less. Further, you should iterate from the end of the list back to the beginning, in order not to run out of range because you're modifying the list (deleting elements) while iterating it.

Modify:

def evens(numbers):
    for i in range(len(numbers), 0):
        if numbers[i] % 2 != 0:
            del numbers[i]
        return numbers

to:

def evens(numbers):
    for i in range(len(numbers)-1, -1, -1): # <-- change the iteration from end to beginning, in order not to run out of index range
        if numbers[i] % 2 != 0:
            del numbers[i]
    return numbers # <-- change in indentation

OUTPUT

[2, 4, 6, 8]
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • 1
    In fact, you have to iterate in reverse order. Otherwise the `del` statement will break the intended iteration order. – Huazuo Gao Sep 19 '15 at 03:34
  • Yes i thought that would be a fix, but then I get a list index out of range error?? – Dude Sep 19 '15 at 03:34
  • Good catch @HuazuoGao - fixed! – Nir Alfasi Sep 19 '15 at 03:36
  • @Dude see the changes above. – Nir Alfasi Sep 19 '15 at 03:39
  • I'm trying to get my head around how the del statement breaks the iteration order.. can someone help explain that to me? Thanks – Dude Sep 19 '15 at 03:58
  • 1
    @Dude by removing elements you're shortening the list. If you deleted index 7, now index 8 becomes index 7. Which means that when you iterate from zero to the full length and delete elements you'll get index out of bounds error. The way to work around it is start from the end, if you delete index 8 and move on backwards - index 7 will not get affected. – Nir Alfasi Sep 19 '15 at 04:00
  • ah so the range length stays locked as the original list length, and doesnt become updated to a smaller range as each iteration removes a value? – Dude Sep 19 '15 at 04:04
  • @Dude not that "it stays locked" but rather: since you go backwards deleting keys will not change the indexes of elements that you'll iterate in the future. Try to run manually a small example (say, list of 3 elements). – Nir Alfasi Sep 19 '15 at 04:09
  • Thanks @alfasin yeh I've tried running a few on paper and I just find that as elements are deleted from the list, the list range also decreases (thus decreasing the amount of iterations in the for loop) and therefore it shouldn't be trying to access indexes which are beyond the loop range. Anyway, must be doing something wrong.. I dont expect you to explain this to me any further! – Dude Sep 19 '15 at 05:09
  • so just to answer my own question for anyone who cares, Ive read that the range of the for loop is created based on the value of the range AT THE TIME IT IS CALLED --anything that happens to the range afterwards is irrelevant. – Dude Sep 19 '15 at 05:53
  • @Dude sorry I thought that it's obvious... otherwise, how would you get out of bound index error ? ;) – Nir Alfasi Sep 19 '15 at 05:56
  • ha yeh it is obvious, but I wanted to know WHY, because the range appeared to contain a value which was variable - i.e len(numbers), but in fact this is a situation where changing the variable won't affect the loop. It also appears there are ways to mutate the range of a for loop during the loop, but that is more complex. – Dude Sep 19 '15 at 05:59
  • @Dude AFAIK you cannot "mutate the range of a for loop" while running, if you know a way to do it I'd appreciate a link! – Nir Alfasi Sep 19 '15 at 06:03
  • http://stackoverflow.com/questions/11905606/changing-the-number-of-iterations-in-a-for-loop some complex stuff towards the bottom of the page that my newb brain doesnt understand – Dude Sep 19 '15 at 06:15
  • @Dude if you're referring to [this example](http://stackoverflow.com/a/11905667/1057429) then it's not a for-loop and it doesn't use `range`. If you were referring to [this answer](http://stackoverflow.com/a/11907651/1057429) then pay attention that the loop doesn't use the `range` - once a `range` was created - it will not change! – Nir Alfasi Sep 19 '15 at 06:18
  • yeh i understand those are while loops, I was referring to the stuff further down that page, but honestly its too complex for me to bother with right now. – Dude Sep 19 '15 at 09:12