关于python:如何使用range()函数从列表中删除项目时迭代列表?

How to iterate a list while deleting items from list using range() function?

本问题已经有最佳答案,请猛点这里访问。

这是我在学习Python编程时遇到的最常见的问题。问题是,当我尝试使用"range()"函数迭代列表时,检查列表中的给定项是否满足给定条件,如果满足,则删除它时,它将始终给出"indexError"。那么,有没有一种特殊的方法可以做到这一点而不使用任何其他中间列表或"while"语句呢?下面是一个例子:

1
2
3
4
l = range(20)
for i in range(0,len(l)):
  if l[i] == something:
    l.pop(i)


首先,您永远不想在Python中迭代类似的事情。迭代实际对象,而不是索引:

1
2
3
l = range(20)
for i in l:
    ...

出现错误的原因是您正在删除一个项,因此以后的索引将不再存在。

现在,您不能在循环遍历列表时修改列表,但这不是问题。更好的解决方案是在这里使用列表理解,过滤掉多余的项目。

1
2
l = range(20)
new_l = [i for i in l if not i == something]

您也可以使用filter()内置,尽管在大多数情况下这一点往往不明确(并且在需要lambda的地方会变慢)。

还要注意,在python 3.x中,range()生成一个生成器,而不是一个列表。

使用更具描述性的变量名也是一个好主意——我在这里假设是这样的,但是像il这样的名称很难阅读,并且更容易引入错误。

编辑:

如果您希望更新现有列表,如注释中所指出的那样,您可以使用切片语法依次替换列表的每个项(l[:] = new_l)。也就是说,我认为这个案子设计得很糟糕。您不希望一段代码依赖于以这种方式从另一位代码更新的数据。

编辑2:

如果出于任何原因,在循环项目时需要索引,这就是enumerate()内置的目的。


你可以用列表理解来做这类事情:

1
newlist=[i for i in oldlist if not condition ]


获取IndexError的原因是在for循环中迭代时更改了列表的长度。基本上,这是逻辑…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-- Build the original list: [0, 1, 2, ..., 19]
l = range(20)

#-- Here, the range function builds ANOTHER list, in this case also [0, 1, 2, ..., 19]
#-- the variable"i" will be bound to each element of this list, so i = 0 (loop), then i = 1 (loop), i = 2, etc.
for i in range(0,len(l)):
    if i == something:
        #-- So, when i is equivalent to something, you"pop" the list, l.
        #-- the length of l is now *19* elements, NOT 20 (you just removed one)
        l.pop(i)
    #-- So...when the list has been shortened to 19 elements...
    #-- we're still iterating, i = 17 (loop), i = 18 (loop), i = 19 *CRASH*
    #-- There is no 19th element of l, as l (after you popped out an element) only
    #-- has indices 0, ..., 18, now.

还要注意,您是根据列表的索引做出"pop"决定的,而不是列表的索引单元格中的内容。这是不寻常的——这是你的意图吗?还是你?意思是更像…

1
2
if l[i] == something:
    l.pop(i)

现在,在您的具体示例中,(l[i] == i)但是这不是一个典型的模式。

尝试使用filter函数,而不是遍历列表。它是一个内置的(就像许多其他列表处理功能一样:例如映射、排序、反转、压缩等等)。

试试这个…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-- Create a function for testing the elements of the list.
def f(x):
    if (x == SOMETHING):
        return False
    else:
        return True

#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where f(l[i]) is True, the element l[i] is kept and will be in the new list, m.
#-- Where f(l[i]) is False, the element l[i] is passed over and will NOT appear in m.
m = filter(f, l)

列表处理函数与"lambda"函数(在python中,这些函数是简短的匿名函数)密切相关。所以,我们可以把上面的代码重写为…

1
2
3
4
5
6
7
#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where lambda is True, the element l[i] is kept and will be in the new list, m.
#-- Where lambda is False, the element l[i] is passed over and will NOT appear in m.
m = filter(lambda x: (x != SOMETHING), l)

试试看它是怎么工作的!


正如其他人所说,迭代列表并创建一个只包含您想要保留的项的新列表。

使用切片分配就地更新原始列表。

1
l[:] = [item for item in l if item != something]


  • 不应使用for i in range(0,len(l)):,如果需要索引,则使用for i, item in enumerate(l):,如果不需要索引,则使用for item in l:
  • 您不应该操纵正在迭代的结构。当需要这样做时,改为迭代一个副本
  • 不要命名变量l(可能被误认为是1或i)
  • 如果要筛选列表,请显式执行。使用filter()或列出理解

顺便说一句,在你的情况下,你也可以这样做:

1
while something in list_: list_.remove(something)

不过,这不是很有效。但根据上下文,它可能更可读。


你应该从另一个角度来看待这个问题:当一个元素与"something"相等时,将它添加到一个列表中。列表理解:

1
l = [i for i in xrange(20) if i != something]