在迭代列表时从列表中删除项目时出现奇怪的结果

问题描述:

我有这段代码:

numbers = range(1, 50)

for i in numbers:
    if i < 20:
        numbers.remove(i)

print(numbers)

但我得到的结果是:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

当然,我希望低于 20 的数字不会出现在结果中.看起来我在删除时做错了什么.

Of course, I'm expecting the numbers below 20 to not appear in the results. Looks like I'm doing something wrong with the remove.

在迭代列表的同时修改列表.这意味着第一次循环时,i == 1,因此从列表中删除了 1.然后 for 循环转到列表中的第二项,它不是 2,而是 3!然后从列表中删除它,然后 for 循环继续到列表中的第三项,现在是 5.依此类推.也许像这样更容易形象化,用 ^ 指向 i 的值:

You're modifying the list while you iterate over it. That means that the first time through the loop, i == 1, so 1 is removed from the list. Then the for loop goes to the second item in the list, which is not 2, but 3! Then that's removed from the list, and then the for loop goes on to the third item in the list, which is now 5. And so on. Perhaps it's easier to visualize like so, with a ^ pointing to the value of i:

[1, 2, 3, 4, 5, 6...]
 ^

这是列表最初的状态;然后 1 被删除,循环转到列表中的第二项:

That's the state of the list initially; then 1 is removed and the loop goes to the second item in the list:

[2, 3, 4, 5, 6...]
    ^
[2, 4, 5, 6...]
       ^

等等.

在迭代列表时没有改变列表长度的好方法.你能做的最好的事情是这样的:

There's no good way to alter a list's length while iterating over it. The best you can do is something like this:

numbers = [n for n in numbers if n >= 20]

或者这个,用于就地更改(括号中的东西是一个生成器表达式,在切片赋值之前隐式转换为元组):

or this, for in-place alteration (the thing in parens is a generator expression, which is implicitly converted into a tuple before slice-assignment):

numbers[:] = (n for in in numbers if n >= 20)

如果你想在删除之前对 n 执行一个操作,你可以尝试的一个技巧是:

If you want to perform an operation on n before removing it, one trick you could try is this:

for i, n in enumerate(numbers):
    if n < 20 :
        print("do something")
        numbers[i] = None
numbers = [n for n in numbers if n is not None]