Анализ
Перепишем код с заменой for
на while
. j
- перебирает индексы в списке. Печать доработана чтобы выводить j
и список:
a = [1, 2, 3, 4, 5, 6]
j = 0
while j < len(a):
i = a[j]
# print(i)
print(f'j = {j}, a = {a}, a[j] = {a[j]}')
if i % 2 != 0:
a.remove(i)
j += 1
Когда условие истинно, список a
укорачивается и счётчик j
увеличивается. Пропускаются элементы:
j = 0, a = [1, 2, 3, 4, 5, 6], a[j] = 1
j = 1, a = [2, 3, 4, 5, 6], a[j] = 3
j = 2, a = [2, 4, 5, 6], a[j] = 5
Копирование исходного списка
Проблема от того что меняется список по которому ведётся цикл. Правим оригинальный цикл. Видите a[:]
в заголовке цикла?. Теперь итерация ведётся по копии списка и все неприятности исчезают:
a = [1, 2, 3, 4, 5, 6]
for i in a[:]:
print(i)
if i % 2 != 0:
a.remove(i)
Точное управление счётчиком в while
Вариант с while
тоже можно доработать. Он обходится без копирования:
a = [1, 2, 3, 4, 5, 6]
j = 0
while j < len(a):
i = a[j]
# print(i)
print(f'j = {j}, a = {a}, a[j] = {a[j]}')
if i % 2 != 0:
a.remove(i)
else:
j += 1
j = 0, a = [1, 2, 3, 4, 5, 6], a[j] = 1
j = 0, a = [2, 3, 4, 5, 6], a[j] = 2
j = 1, a = [2, 3, 4, 5, 6], a[j] = 3
j = 1, a = [2, 4, 5, 6], a[j] = 4
j = 2, a = [2, 4, 5, 6], a[j] = 5
j = 2, a = [2, 4, 6], a[j] = 6
Хотя всё это работает, я бы не стал так делать. Работает медленно на длинных списках (квадратичная сложность в терминах О-большого). А последний код ещё и сложно устроен.
Классика
Индекс j
будет отставать от итераций в цикле for
всякий раз когда очередное значение в списке нечётное. В этом случае список тоже меняется во время итерации, но не меняется его длина и "хвост". Этого достаточно, чтобы сюрпризов не было.
Так как во время итерации мы не меняем длину списка в конце его надо обрезать (del
):
a = [1, 2, 3, 4, 5, 6]
j = 0
for i in a:
print(i)
if i % 2 == 0:
a[j] = i
j += 1
del a[j:]
Классика на Питоне
В Питоне есть ещё один способ отфильтровать список "на месте". К сожалению, тут нужна двойная память: выражение a[:] = (...)
создаёт список из генератора в правой части:
a = [1, 2, 3, 4, 5, 6]
print(id(a), a)
a[:] = (i for i in a if i % 2 == 0)
print(id(a), a)
139721591755968 [1, 2, 3, 4, 5, 6]
139721591755968 [2, 4, 6]
Не удивлюсь если этот вариант окажется самым быстрым - меньше кода, быстрее работает интерпретатор.