【问题标题】:Even if I use reversed range, still 'list index out of range in Python' happen即使我使用反向范围,仍然会发生“Python 中的列表索引超出范围”
【发布时间】:2020-05-12 04:00:29
【问题描述】:

我发现当我在 for 循环中使用 'pop' 时,它可能会出错,例如 'list index out of range in Python'。因为删除一个索引会使列表变短。

并且还发现如果我使用某种反转范围,我可以修复这个错误。 但是当我尝试应用我在 Stack Overflow 上学到的知识时,我仍然无法解决这个问题。

例如

candidates = []
def make_candidates(user_group, ban_group):
    for i in range(len(user_group)-1, -1, -1):   
        for j in range(len(ban_group)):
            if len(user_group[i]) == len(ban_group[j]):
                candidates.append(user_group.pop(i))

    return candidates

print(make_candidates(["frodo", "fradi", "crodo", "abc123", "frodoc"], ["*rodo", "*rodo", "******"]))  

范围是 (4, -1, -1),所以 'i' 是 4 到 0。

当 'i=4, j=2', len(user_gorup[4]) == len(ban_group[2])) -> 候选者 = ["frodoc"] -> user_group = ["frodo", " fradi", "crodo", "abc123"]

当 'i=3, j=2', len(user_group[3]) == len(ban_group[2])) -> 候选人 = ["frodoc", "abc123"] -> user_group = [" frodo”、“fradi”、“crodo”]

这样,就没有 out of list 索引了。当范围变短时,列表也变短,所以没有问题。不是吗?为什么还会出现错误?

【问题讨论】:

  • 这个算法试图做什么?返回一组禁止候选人?
  • 您尝试执行的实际计算是什么?您真的需要更改user_group 吗? candidates 之后应该是什么?
  • 在循环遍历列表时,切勿使用要销毁的列表的索引。将第一个 for 循环替换为 while 循环:while user_group: 然后 user = user_group.pop() 并在有 user_group[i] 或 user_group.pop() 的任何地方使用 user

标签: python list indexing


【解决方案1】:

range 没有魔法。它不与for 语句交互——它只提供一个数值序列来使用。

在外部循环for i in range(len(user_group)-1, -1, -1): 中,range 对象在任何循环发生之前提前创建一次。第一次通过时,我们使用索引i == 4,然后是i == 3,以此类推。

您的期望是,即使列表被缩短,i 仍然是该列表的有效索引。但显然事实并非如此。仔细观察 - j 有很多机会可以删除内部循环中的项目,因此在 i 减少之前,列表可能是 .popped 不止一次。

但即便如此 - 假设只删除了一项 - i 现在实际上是您想要的索引?像这样的逻辑非常棘手,没有理由做任何如此棘手的事情。根本没有理由使用索引 - 您应该直接引用列表元素,并且在完成之前不要尝试删除任何内容。

先计算candidates,然后如果需要,您可以计算出一个单独的非候选用户列表。

我将通过使用函数为关键步骤命名来简化代码,然后使用列表推导而不是循环来进行处理 - 这使您不必考虑.append 和程序逻辑,并且让您只描述您想要的列表。

def should_ban(user, ban_group):
    # I don't understand the reason for comparing name lengths, but
    # this should do the same as your original code tried to do:
    return any(len(user) == len(ban) for ban in ban_group)

def ban_candidates(user_group, ban_group):
    return [user for user in user_group if should_ban(user, ban_group)]

# Then we can filter the bans out of the user group:
def unbanned(user_group, banned):
    return [user for user in user_group if user not in banned]

【讨论】:

  • 哇,非常感谢。问题是由“j”引起的。这种逻辑是错误的和毫无意义的。它是在我试图做出一些有效的逻辑时制作的。我知道这是错误的方式,但想知道为什么会出错。不过,我不得不提出另一个逻辑,无论如何,感谢你,我学到了范围的重要特征。祝你好运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多