我有一个奇怪的问题。有人看到我的代码有什么问题吗?
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循环跳过它。有什么想法吗?
答案 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。