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

时间:2012-05-10 17:44:30

标签: python

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

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

6 个答案:

答案 0 :(得分:7)

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

l = range(20)
for i in l:
    ...

您的错误原因是您要删除某个项目,因此后来的索引不再存在。

现在,您在循环时无法修改列表,但这不是问题。更好的解决方案是在这里使用list comprehension来过滤掉额外的项目。

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

你也可以使用the filter() builtin,虽然在大多数情况下这种情况往往不明确(在需要lambda的地方较慢)。

另请注意,在Python 3.x中,range()生成一个生成器,而不是列表。

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

编辑:

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

编辑2:

如果出于任何原因,在循环项目时需要索引,那就是the enumerate() builtin的用途。

答案 1 :(得分:2)

你总是可以通过列表理解来做这件事:

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

答案 2 :(得分:0)

你应该从另一方面看问题:当它与“某事”相等时,将一个元素添加到列表中。列表理解:

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

答案 3 :(得分:0)

  • 您不应该使用for i in range(0,len(l)):,如果您需要索引,请使用for i, item in enumerate(l):,如果不是,请使用for item in l:
  • 你不应该操纵你正在迭代的结构。当面临这样做时,重复一遍副本
  • 不要将变量l命名(可能会误认为是1或I)
  • 如果要过滤列表,请明确执行此操作。使用filter()或列表推导
顺便说一下,在你的情况下,你也可以这样做:

while something in list_: list_.remove(something)
但是,这不是很有效率。但根据具体情况,它可能更具可读性。

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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

#-- 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.

另请注意,您正在根据列表的索引做出“弹出”决定,而不是列表的索引单元格中的内容。这很不寻常 - 是你的意图吗?或者你呢 意思是更像......

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

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

尝试使用过滤器功能,而不是迭代列表。它是一个内置的(像很多其他列表处理函数:例如map,sort,reverse,zip等)。

试试这个......

#-- 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中,这些函数是简短的匿名函数。所以,我们可以将上面的代码重写为......

#-- 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)

试一试,看看它是如何运作的!