Python: индексация списка для сбоя удаления цикла

Это очень сложная проблема для объяснения. Я играл с методами списка Python, особенно с индексным объектом del. Я хотел создать простой скрипт, который создавал бы список целых чисел от 1 до 100, а затем цикл for, который удалял бы нечетные числа из списка.

Вот сценарий, который я написал:

def main():
    num = list(range(1,101))
    print(num)
    for i in range(0,101):
        del num[i]
    print(num)
main()

Кажется, это сработает? Я тоже так думал, пока не запустил.

Не знаю почему, но когда i передавалось в индекс del num[i], само число удваивалось.

Когда я запустил его, я получил IndexError: list assignment index out of range.

Когда я изменил параметры с range(0,101) на range(0,10), я обнаружил, что он удалил все нечетные числа от 1 до 20.

Другими словами, i в индексе удваивается, хотя этого не должно быть. Могу ли я получить некоторую информацию об этом?


person Oleg Silkin    schedule 17.08.2015    source источник
comment
И теперь вы усвоите важный урок программирования: не изменяйте объекты, пока вы их перебираете. :)   -  person Two-Bit Alchemist    schedule 17.08.2015
comment
Разве что можно, на таких языках как go :)   -  person Filip Haglund    schedule 17.08.2015
comment
Вот мой ответ на недавний вопрос, который может быть полезен: title="list removeitem в python, как это работает"> stackoverflow.com/questions/32043963/   -  person Cyphase    schedule 17.08.2015
comment
@FilipHaglund, хм, ты уверен? Что ты имеешь в виду?   -  person Cyphase    schedule 17.08.2015
comment
он работает с копией переменной, переданной в range, но, поскольку срезы являются указателями на базовый массив, он не работает так, как я ожидал. Виноват :)   -  person Filip Haglund    schedule 17.08.2015


Ответы (5)


Когда вы используете ключевое слово del внутри цикла for, оно полностью удаляет элемент из исходного списка num, который вы создали. Таким образом, длина вашего списка становится все меньше и меньше с каждой итерацией цикла.

Это также объясняет, почему он удаляет только нечетные числа, поскольку индексы смещаются вниз после каждого удаления, позволяя четным числам проскользнуть сквозь трещины вашей программы:

num = range(1, 5)  # num = [1, 2, 3, 4]
del num[0]  # results in num = [2, 3, 4]
del num [1]  # deletes 3, and num now = [2, 4]
etc. for a longer loop

Чтобы удалить нечетные числа, рассмотрите возможность использования условного оператора для проверки статуса %2 == 0 и метода list.remove():

num = range(1, 101)
for i in num:
    if i % 2 != 0:
        num.remove(i)
print(num)

Или понимание списка внутри вашей функции main():

return [x for x in num if x % 2 == 0]
person bobbyz    schedule 17.08.2015
comment
О боже, ты совершенно прав. Я думал, что список будет хранить удаленные значения где-то во время индекса. Это полностью объяснило мне это. Спасибо приятель! - person Oleg Silkin; 17.08.2015
comment
Я имею в виду, что я дошел до того, что разделил его пополам и заставил его работать, но спасибо за объяснение, почему это не работает. - person Oleg Silkin; 17.08.2015
comment
Ничего страшного, рад, что помогло :) - person bobbyz; 24.08.2015

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

Вот имитация запуска:

>>> a = [1, 2, 3, 4, 5]
>>> a
[1, 2, 3, 4, 5]
>>> del a[0]
>>> a
[2, 3, 4, 5]
>>> del a[1]
>>> a
[2, 4, 5]
>>> del a[2]
>>> a
[2, 4]
>>> del a[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
person Filip Haglund    schedule 17.08.2015

Вы перебираете диапазон от 0 до 101 и удаляете один элемент из num на каждой итерации, поэтому длина num уменьшается, и в итоге вы получаете IndexError.

person svfat    schedule 17.08.2015

Эта проблема очень похожа на mutating и list. При повторении одного и того же списка перейдите по ссылке ниже, которая поможет вам лучше понять эту ситуацию.

http://gsb-eng.com/why-python-list-mutation-is-not-a-good-idea/

person gsb-eng    schedule 17.08.2015

для второго цикла for вы должны использовать половину общих индексов, потому что на 50-й итерации будет удалено 50 элементов, поэтому общий индекс списка уменьшается до 50, поэтому на 51-й итерации он отображает из ошибка диапазона.

person RAKESH B N    schedule 06.05.2021