为什么在按索引弹出或删除时会出现IndexError?

时间:2015-05-07 20:30:35

标签: python arrays list python-2.7 python-3.x

你能告诉我为什么我必须在try / except语句中包含这个if吗? 当我使用list调用函数时,此方法引发IndexError,其中最后一个元素与列表中的其他元素相同。当最后一个元素在列表中只显示一次时,不会引发IndexError。

def unique(n):
    for i in range(len(n) - 1, -1, -1):
        for j in range(i):
            try:
                if n[i] == n[j]:
                    n.pop(i)
            except:
                pass
    return n

>>unique([1, 2, 3, 2, 4])
[1, 2, 3, 4]
>>unique([1, 2, 3, 4, 2])
IndexError

if语句中没有try / except:

>>print n[i]
2
>>n.pop(i)
IndexError
>>del n[i]
IndexError

同样围绕n.pop(i)使用try / except并不起作用,我不得不用它包围整个if。为什么?

2 个答案:

答案 0 :(得分:1)

当您迭代迭代时,您正在更改迭代,这从根本上是错误的并且可能导致未定义的行为。这样做的行为是您正在修改列表长度而不通知迭代器更改。迭代次数仍然等于列表的原始长度,但实际的元素数量可能更低(如果没有重复,则可以)。

访问Python中不存在的索引通常会引发IndexError。你的try / except块会抑制它,但迭代仍将继续超过原始长度。

可能的解决方案实际上是使用一个集合,它将为您处理唯一性:

def unique(n):
    return list(set(n))

或者,保留您的迭代解决方案,创建一个空列表或您将分配结果的列表副本。为了减少时间复杂度,可以将唯一数据存储为dict,其中赋值是恒定时间:

def unique(n):
    uniques = {}

    for i in range(len(n) - 1, -1, -1):
        uniques[n[i]] = n[i]

    return list(uniques.keys())

如果您关心按键外观的顺序,OrderedDict可以派上用场。

from collections import OrderedDict

def unique(n):
    return list(OrderedDict.fromkeys(n))

答案 1 :(得分:1)

我不相信你的

>>print n[i]
2
>>n.pop(i)
IndexError

示范。无论如何,当你发现索引i包含重复并删除它时,你也可以打破内循环,而不是try / except。毕竟,这是内循环的唯一工作。然后你也不会得到错误,这恰好来自于你已经删除它后继续并尝试访问n[i]

def unique(n):
    for i in range(len(n) - 1, -1, -1):
        for j in range(i):
            if n[i] == n[j]:
                n.pop(i)
                break
    return n