简体   繁体   English

令人困惑的Python语句行为

[英]Confusing behavior of Python for statement

I have the following python code: 我有以下python代码:

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? 第一行和最后一行是正确的,但是为什么没有打印出2、4、6和8? The print statement is not inside the if statement! 打印语句不在if语句内!

I'm using python(x,y) on windows 7. Also, I'm new to Python...I'm used to C++ 我在Windows 7上使用python(x,y)。此外,我是Python的新手...我已经习惯了C ++

You're removing items from the list ( x.remove ) while iterating over it ( for number in x ). 您要从列表( x.remove )删除项目,同时对其进行迭代( for number in x )。

for - in maintains an index separately, and that is why modifying the list gives unexpected behavior. for - in单独维护一个索引,这就是为什么修改列表会产生意外行为的原因。

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: 为了清楚起见,添加一些print语句:

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. 如您所见,即使您从列表中删除元素, index将继续增加1 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. 您可以简单地使用range步骤来仅创建偶数,但是上述解决方案使您可以在任何条件下进行过滤。

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. 当您删除1 ,您可以想象所有值都被移动了一个位置,迭代器指向2以前的位置,但是现在该值是3 ,因此永远不会打印2 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. 正如Mark Rushakoff提到的那样,在迭代过程中不应修改某些内容。 Change your for number in x to for number in x[:] and it will work as you expect, though. for number in xfor number in x[:]更改for number in x for number in x[:] ,尽管如此,它仍然可以正常工作。 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 want 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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