Python для цикла, пропускающего любой другой цикл? - PullRequest
4 голосов
/ 16 марта 2012

У меня странная проблема. Кто-нибудь видит что-то не так с моим кодом?

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 ]

12 голосов
/ 16 марта 2012

Вы изменяете содержимое объекта forms, который вы повторяете, когда говорите:

forms.remove((form,question))

Согласно документации Python оператора for , это небезопасно (ударение мое):

Оператор for в Python немного отличается от того, к чему вы привыкли в 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. Выбор за вами ...

4 голосов
/ 16 марта 2012

Вы удаляете объекты из forms, перебирая его.Это должно привести к поведению, которое вы видите (http://docs.python.org/reference/compound_stmts.html#the-for-statement).

Решение состоит в том, чтобы перебрать копию этого списка или добавить формы для удаления в отдельную коллекциюзатем выполните удаление.

2 голосов
/ 16 марта 2012

Использование метода удаления в формах (я предполагаю, что это список) изменяет размер списка. Так что подумай об этом

[ 50, 51, 52 ]

Является ли ваш первоначальный список, и вы просите первый пункт. Затем вы удаляете этот элемент из списка, чтобы он выглядел как

[51, 52]

Но теперь вы просите второй предмет, и вы получаете 52.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...