【问题标题】:Python for loop skipping every other loop?Python for循环跳过所有其他循环?
【发布时间】:2012-04-02 02:56:24
【问题描述】:

我有一个奇怪的问题。有人看到我的代码有什么问题吗?

for x in questions:
    forms.append((SectionForm(request.POST, prefix=str(x.id)),x))
    print "Appended " + str(x)
for (form, question) in forms:
    print "Testing " + str(question)
    if form.is_valid():
        forms.remove((form,question))
        print "Deleted " + str(question)
        a = form.save(commit=False)
        a.audit = audit
        a.save()                
    else:
        flag_error = True

结果:

Appended Question 50
Appended Question 51
Appended Question 52
Testing Question 50
Deleted Question 50
Testing Question 52
Deleted Question 52

似乎跳过了问题 51。它被附加到列表中,但 for 循环跳过了它。有什么想法吗?

【问题讨论】:

    标签: python django


    【解决方案1】:

    当你说:

    forms.remove((form,question))
    

    根据Python documentation of the for statement,这是不安全的(重点是我的):

    Python 中的 for 语句与您在 C 或 Pascal 中可能使用的语句有些不同。 Python 的 for 语句迭代任何序列的项目(列表或一个字符串),按照它们在序列中出现的顺序。

    在循环中修改被迭代的序列是不安全的(这只会发生在可变序列类型上,例如列表)。如果您需要修改您正在迭代的列表(例如,复制所选项目),您必须迭代一个副本。切片符号使这变得特别方便:

    for x in a[:]: # make a slice copy of the entire list
    ...    if len(x) > 6: a.insert(0, x)
    

    另请参阅Python Language Reference 中的这一段,它准确地解释了正在发生的事情:

    当序列被循环修改时有一个微妙之处(这只会发生在可变序列,即列表中)。内部计数器用于跟踪接下来使用哪个项目,并在每次迭代时递增。当此计数器达到序列的长度时,循环终止。 这意味着如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取已处理的当前项目的索引)。 同样,如果套件在当前项目之前插入序列中的项目,则当前项目将在下一次循环中再次处理。

    有很多解决方案。您可以按照他们的建议创建副本。另一种可能性是作为第二个for 循环的结果创建一个新列表,而不是直接修改forms。选择权在你...

    【讨论】:

    • 哪里说不允许这样做?
    • 所以在这种情况下,我建议标记要删除的问题(或保留要删除的问题的索引列表)并在循环后实际删除它们。
    • @Marcin,它说它不安全,而不是不允许它。 Python 试图将用户视为负责任的成年人:如果您知道它不安全并选择继续,则后果自负,但当它没有达到您的预期时不要来抱怨。
    • @Duncan 请注意,答案可能会发生变化。我的评论已有 10 分钟;答案是 4 分钟前的当前形式,因为 JustinEthier 已更正它。在任何情况下,python 在这种情况下的行为都不是未定义的,尽管教程说了什么,但它(很大程度上)是安全的,并且(绝对)明确定义了从列表中删除元素时会发生什么。
    • @JustinEthier 行为在这里指定:docs.python.org/reference/compound_stmts.html#the-for-statement;其他实现是否表现出相同的行为是它们是否符合规范的一个方面。
    【解决方案2】:

    您正在从forms 中删除对象,同时对其进行迭代。这应该会导致您看到的行为 (http://docs.python.org/reference/compound_stmts.html#the-for-statement)。

    解决方案是遍历该列表的副本,或者将要删除的表单添加到单独的集合中,然后再执行删除。

    【讨论】:

      【解决方案3】:

      在表单(我假设它是一个列表)上使用 remove 方法会更改列表的大小。所以这样想

      [ 50, 51, 52 ]
      

      是你的初始清单,你要的是第一项。然后,您从列表中删除该项目,所以它看起来像

      [51, 52]
      

      但现在你要求第二个项目,所以你得到 52。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-05
        • 2013-02-01
        • 2021-08-23
        • 1970-01-01
        相关资源
        最近更新 更多