3

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

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

6 回答 6

7

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

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

您的错误的原因是您正在删除一个项目,因此后来的索引不复存在。

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

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

您也可以使用builtin filter()尽管在大多数情况下往往不清楚(并且在您需要的地方更慢lambda)。

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

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

编辑:

如果您希望更新现有列表,如评论中所指出的,您可以使用切片语法依次替换列表中的每个项目(l[:] = new_l)。也就是说,我认为这种情况是非常糟糕的设计。您不希望一段代码依赖以这种方式从另一段代码更新的数据。

编辑2:

如果出于任何原因,您在遍历项目时需要索引,这就是内置enumerate()函数的用途。

于 2012-05-10T17:47:15.477 回答
2

你总是可以用列表理解来做这种事情:

newlist=[i for i in oldlist if not condition ]
于 2012-05-10T17:47:44.057 回答
0

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

l = [i for i in xrange(20) if i != something]
于 2012-05-10T17:48:58.933 回答
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)

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

于 2012-05-10T17:50:06.750 回答
0

正如其他人所说,遍历列表并创建一个仅包含您想要保留的项目的新列表。

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

l[:] = [item for item in l if item != something]
于 2012-05-10T17:52:44.740 回答
0

你得到 an 的原因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)但这不是典型的模式。

与其遍历列表,不如试试 filter 函数。它是一个内置的(就像许多其他列表处理功能:例如 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)

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

于 2012-05-10T18:02:40.827 回答