简体   繁体   中英

Confusing behavior of Python for statement

I have the following python code:

x = range(0,10)
print x

for number in x: 
    print(number)
    if number%2<> 0: 
        x.remove(number)

print x

Oddly, the out put is this:

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

The first and last lines are right, but why are 2,4,6, and 8 not printed out? The print statement is not inside the if statement!

I'm using python(x,y) on windows 7. Also, I'm new to Python...I'm used to C++

You're removing items from the list ( x.remove ) while iterating over it ( for number in x ).

for - in maintains an index separately, and that is why modifying the list gives unexpected behavior.

The list is iterated using its index, but when you remove elements you skip some indices.

Eg:

[0,1,2,...] # (iterator is at second - print 1)

remove

[0,2,3,...] # (iterator is still at second)

iterator advances

[0,2,3,...] # (iterator is at third - print 3)

Add some print statements for clarity:

x = range(10)

for index, number in enumerate(x):
    print "x is      ", x
    print "element is", number
    print "index is  ", index
    print

    if number % 2 == 0: 
        x.remove(number)

And the output:

x is       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
element is 0
index is   0

x is       [1, 2, 3, 4, 5, 6, 7, 8, 9]
element is 2
index is   1

x is       [1, 3, 4, 5, 6, 7, 8, 9]
element is 4
index is   2

x is       [1, 3, 5, 6, 7, 8, 9]
element is 6
index is   3

x is       [1, 3, 5, 7, 8, 9]
element is 8
index is   4

As you can see, index keeps going up by 1 , even though you remove elements from the list. This is what causes the loop to skip elements.

As others have pointed out, looping over a list and removing elements from it isn't a good idea. Loop over a copy instead:

for number in x[:]:

Or:

for number in list(x):

Better yet, make a new list with a list comprehension:

[number for number in x if number % 2 == 0]

Basically you can have weird behavior when you iterate something while removing at the same time. What's happening is that you're skipping some values due to them being shifted to indexes that you already iterated over.

A better way of doing what you want (filter out some items), would be to use a list comprehension, for instance:

[x for x in range(10) if x%2==0]

You could simply use the range step to only create even numbers, but the above solution let's you filter out on any condition.

The reason why some numbers aren't printed is that the values are changing positions while you loop and remove them. When you remove the 1 , you can imagine all the values being shifted by one position, the iterator is pointing to where the 2 used to be, but now the value there is 3 , so the 2 is never printed. And this goes on for the rest of the values.

As Mark Rushakoff mentions, you shouldn't modify something while you're iterating over it. Change your for number in x to for number in x[:] and it will work as you expect, though. In this case you're iterating over a copy.

Don't modify a list you're iterating over. Others suggest copying the list or collecting a new list of things to remove. Instead, collect the ones you to remain. 保留的物品。 This is faster than copying the list and removing from the copy not being iterated over, and faster than collecting the ones to remove and then removing them.

    evens = []
    for number in x:
        if number%2 == 0:
            evens += [number]
    print(evens)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM