2012-03-16 205 views
4

我有一个奇怪的问题。有没有人看到我的代码有什么问题?Python for循环跳过其他循环?

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

回答

11

要修改的对象forms的,你是迭代的内容,当你说:

forms.remove((form,question)) 

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

Python中的for语句与C或Pascal中的for语句有所不同。 Python的for语句并不总是迭代数字的算术级数(比如在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。选择取决于你...

+0

它在哪里说,这是不允许的? – Marcin 2012-03-16 14:50:50

+0

因此,在这种情况下,我会建议标记要删除的问题(或保留要删除的问题的索引列表),并在循环后实际删除它们。 – egor83 2012-03-16 14:52:14

+0

好点,它不安全,但允许。 – 2012-03-16 14:52:35

2

使用窗体上的remove方法(我认为是一个列表)更改列表的大小。所以想想这样

[ 50, 51, 52 ] 

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

[51, 52] 

但现在你问的第二个项目,所以你得到52