0

So I have this code:

animals = ['cat', 'dog', 'waffle', 'giraffe', 'turtle']
breakfeast_foods = ['waffle', 'pancake', 'eggs']
for index, item in enumerate(animals):
    print item
    if item in breakfeast_foods:
        animals.pop(index)

And for some reason the loop print statement does not print 'giraffe.' I don't know why, is there something I'm missing?

Jeremy Spencer
  • 250
  • 1
  • 6
  • 15
  • Is this homework? There is a better way with `if item not in...` – Douglas B. Staple Jun 10 '14 at 00:48
  • It may have been for someone else's homework, but it isn't for mine. Someone else had actually asked me how to do this (remove certain items from a list) and this was my response. But after I inspected the code more carefully I found this oddity, so I decided to ask why it happened. – Jeremy Spencer Jun 10 '14 at 00:56

1 Answers1

5

First things first, it's actually breakfast rather than breakfeast. What you're breaking (ending) is the fast (going without food) rather than the feast (having lots of food).

But that's just me being pedantic :-)


Now, on to the actual question. You should not modify the list while enumerating it (technically, you are allowed to do this but you will get strange results like you've seen).

Doing so messes up the internal structures used for the enumeration. This is a simplified explanation (a) but it should illustrate the concept:

Think of the enumeration as just stepping through each element in animals using the index.

When you get to index 2 (waffle) and you find it's in breakfast_foods, you remove the item at that index and shuffle the other ones along so that you now have:

['cat', 'dog', 'giraffe', 'turtle']

Then the enumeration moves on to index 3 (which is now turtle) and you print that out, skipping giraffe in the process.

In any case, perhaps a more Pythonic way of doing this would be:

animals = [x for x in animals if x not in breakfast_foods]

(a) Whether a particular implementation of Python uses simple arrays or other data structures is not really important here. The explanation is simply to illustrate that changing a data structure while iterating over it can often cause weirdness.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 2
    -1. Bringing up pointers in a Python explanation is not helpful. – Steven Rumbalski Jun 10 '14 at 00:51
  • 1
    @Steven, have you seen the CPython codebase? :-) In any case, I'll defer to your judgement and use a different term. – paxdiablo Jun 10 '14 at 00:54
  • 2
    How else could it be explained, @Steven? – Paul Jun 10 '14 at 00:54
  • 2
    I see, so when I used .pop(index) to remove 'waffle', it shifted 'giraffe' into the spot 'waffle' was in, and because the loop was still running, the Python interpreter thought it had already iterated over 'giraffe'? – Jeremy Spencer Jun 10 '14 at 00:54
  • 1
    @Jeremy, yes, that's basically it. – paxdiablo Jun 10 '14 at 00:55
  • @paxdiablo: Instead of pointers try "indexes", "one less item", "shifts location", whatever. Python is *implemented* in C, but so are a lot of other languages. And Python is not always implemented in C. Python deserves to be described in it's own terms (unless your audience knows the other the language you're referring to). – Steven Rumbalski Jun 10 '14 at 00:57
  • @Steven, I've changed it to simply "internal structures" so as to cover all possibilities :-) – paxdiablo Jun 10 '14 at 00:59
  • plus 1 for being pedantic =) – binford Apr 30 '20 at 19:26