删除列表项时出现意外的IndexError

时间:2013-10-11 06:56:47

标签: python list python-2.7

我是Python的初学者。我之前学过其他语言,比如C ++(初学者)和JQuery。但我发现python中的循环非常混乱。

我想要实现一个简单的结果。程序将遍历一个单词列表,然后它将删除与 first 两个字母匹配的单词和列表中的下一个单词:

test = ['aac', 'aad', 'aac', 'asd', 'msc']
for i in range(len(test)):
    if test[i][0:2] == test[i+1][0:2]:
        test.remove(test[i])

# This should output only ['aac', 'asd', 'msc']
print test

上述代码应从列表中删除'aac''aad'。但实际上,这提高了IndexError。而且,我无法达到预期的效果。你能解释一下吗?

4 个答案:

答案 0 :(得分:2)

当您从列表中删除项目时,range(len(test))仍保持相同的值。因此,即使您的test列表中只剩下没有项目,循环仍然存在。

我有两个解决方案:

  1. 将您想要的项目复制到新列表中,而不是删除它:

    test2 = test[i]
    

    不要忘记改变条件。

  2. 将其向后循环。像这样:

    n = len(test)
    for i in range(n):
        j = n - i - 1
        if j > 1:
        if test[j][0:2] == test[j-1][0:2]:
            test.remove(test[j])
    

    或者,正如martijn所说:

    n = len(test)
    for i in range(n-1, 0, -1):
        if i > 1:
        if test[i][0:2] == test[i-1][0:2]:
            test.remove(test[i])
    
  3. 希望它有所帮助!

    对不起我以前的愚蠢回答

答案 1 :(得分:2)

在循环到列表起始长度的范围内时,您正在更改列表的长度;从列表中删除一个项目,最后一个索引不再有效。

切换,因为项目从当前索引的列表中删除,列表索引的其余部分 shift ;索引i + 1处的内容现在位于索引i,您的循环索引不再有用。

最后但并非最不重要的是,您正在循环到test的最后一个索引,但之后仍尝试访问test[i + 1];即使您没有从列表中删除元素,该索引也不存在。

您可以使用while循环来实现您想要的目标:

test = ['aac', 'aad', 'aac', 'asd', 'msc']
i = 0
while i < len(test) - 1:
    if test[i][:2] == test[i+1][:2]:
        del test[i]
        continue
    i += 1

现在i针对每个循环迭代的 new 长度进行测试,如果没有删除任何元素,我们只会增加i。请注意,循环限制为长度减去1 ,因为您希望每次迭代测试test[i + 1]

请注意,我使用del test[i];无需扫描列表,搜索值再次;如果值在列表中出现多次,但只有以后的实例应该被删除,这可能会导致细微的错误;例如['aac', 'foo', 'aac', 'aad']会导致['aac', 'foo', 'aad'] ['foo', 'aac', 'aad'],这是test.remove(test[i])会产生的结果。

演示:

>>> test = ['aac', 'aad', 'aac', 'asd', 'msc']
>>> i = 0
>>> while i < len(test) - 1:
...     if test[i][:2] == test[i+1][:2]:
...         del test[i]
...         continue
...     i += 1
... 
>>> test
['aac', 'asd', 'msc']

您可以使用列表解析来避免收缩列表问题:

>>> [t for i, t in enumerate(test) if i == len(test) - 1 or t[:2] != test[i + 1][:2]]
['aac', 'asd', 'msc']

这两种方法只需要一个循环通过输入列表。

答案 2 :(得分:1)

正如其他人所说,当您删除项目时,列表会变短,从而导致索引错误。

与原始问题保持一致。如果你想使用list.remove()删除项目,你可以将找到的项目添加到列表中然后迭代它们并将其从原始列表中删除,如下所示:

# Set up the variables
test = ['aac', 'aad', 'aac', 'asd', 'msc']
found = []
# Loop Over the range of the lenght of the set
for i in range(len(test)):
    try:
        if test[i].startswith(test[i+1][0:2]):
            found.append(test[i])  # Add the found item to the found list
    except IndexError: # You'll hit this when you do test[i+1]
        pass

# Remove the Items at this point so you don't cause any issues
for item in found:
    test.remove(item)  # If an item has been found remove the first instance

# This sholuld output only ['aac', 'asd', 'msc']
print test

编辑:

根据Martins评论,您不需要制作需要删除的第二个项目列表,而是可以创建一个不需要删除的项目列表,如下所示:

# Set up the variables
test = ['aac', 'aad', 'aac', 'asd', 'msc']
found = []

# Loop Over the range of the lenght of the set
for i in range(len(test)):
    try:
        if not test[i].startswith(test[i+1][0:2]):
            found.append(test[i])  # Add the found item to the found list
    except IndexError: # You'll hit this when you do test[i+1]
        found.append(test[i]) # If there is no test[i+1], test[i] must be cool.


# This sholuld output only ['aac', 'asd', 'msc']
print found

答案 3 :(得分:0)

for i in range(len(test))会为您提供一个有效索引为test的列表。但是,当您继续从循环中的test删除项目时,test的大小会减少,导致其中一些最初有效的索引无效。

你正在做的是这样的事情:

L = range(len(test))
for i in L:
  if condition:
    # remove something from test <- the size of test has changed.
                                 # L[-1] is no longer a valid index in test

你可以做的是积累你想要删除的东西的索引并在以后删除它们:

deleteThese = set()
for i,item in enumerate(test[:-1]):
  if item[0:2] == test[i+1][0:2]:
    deleteThese.add(i)
test = [item for i,item in enumerate(test) if i not in deleteThese]

<强>输出

In [70]: test = ['aac', 'aad', 'aac', 'asd', 'msc']

In [71]: %paste
deleteThese = set()
for i,item in enumerate(test[:-1]):
  if item[0:2] == test[i+1][0:2]:
    deleteThese.add(i)
test = [item for i,item in enumerate(test) if i not in deleteThese]

## -- End pasted text --

In [72]: test
Out[72]: ['aac', 'asd', 'msc']