Python for循环跳过每个其他循环?

时间:2012-03-16 14:46:37

标签: python django

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

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循环跳过它。有什么想法吗?

3 个答案:

答案 0 :(得分:12)

您正在修改正在迭代的对象forms的内容,当您说:

forms.remove((form,question))

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

  

Python中的for语句与您在C或Pascal中使用的语句略有不同。而不是总是迭代数字的算术级数(如在Pascal中),或者让用户能够定义迭代步骤和暂停条件(如C),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。选择取决于你...

答案 1 :(得分:4)

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

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

答案 2 :(得分:2)

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

[ 50, 51, 52 ]

是您的初始列表,您要求第一个项目。然后,您从列表中删除该项,因此它看起来像

[51, 52]

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