Как эксперимент, я сделал это:
letters=['a','b','c','d','e','f','g','h','i','j','k','l']
for i in letters:
letters.remove(i)
print letters
Последняя печать показывает, что не все объекты были удалены? (любой был).
IDLE 2.6.2
>>> ================================ RESTART ================================
>>>
['b', 'd', 'f', 'h', 'j', 'l']
>>>
Каково объяснение этого? Как это могло это быть переписанным для удаления каждого объекта?
Некоторые ответы объясняют, почему это происходит, а некоторые объясняют, что вы должны были сделать. Я беззастенчиво складываю кусочки воедино.
Потому что язык Python предназначен для обработки этого варианта использования по-другому. В документации поясняется:
Небезопасно изменять последовательность, по которой выполняется итерация в цикле (это может происходить только для изменяемых типов последовательностей, таких как списки). Если вам нужно изменить список, который вы перебираете (например, чтобы дублировать выбранные элементы), вы должны перебирать копию .
Акцент мой. См. Дополнительную информацию на связанной странице - документация защищена авторским правом, и все права защищены.
Вы можете легко понять, почему вы получили то, что получили, но в основном это неопределенное поведение , которое можно легко изменить без предупреждения от сборки к сборке. Просто не делай этого.
Это как удивляться, почему i + = i ++ + ++ i
делает то, черт возьми, это строка делает с вашей архитектурой в конкретной сборке вашего компилятора для вашего языка - включая, помимо прочего, уничтожение вашего компьютера и , заставляющее демонов вылетать из вашего носа :)
del письма [:]
(если нужно изменить все ссылки на этот объект) букв [:] = []
(если нужно изменить все ссылки на этот объект) букв = []
(если вы просто хотите работать с новым объектом) Может быть, вы просто хотите удалить некоторые элементы в зависимости от условия? В этом случае вам следует перебрать копию списка.Самый простой способ сделать копию - создать фрагмент, содержащий весь список, с синтаксисом [:]
, например:
#remove unsafe commands
commands = ["ls", "cd", "rm -rf /"]
for cmd in commands[:]:
if "rm " in cmd:
commands.remove(cmd)
Если ваша проверка не особенно сложна, вы можете (и, вероятно, должны) вместо фильтра:
commands = [cmd for cmd in commands if not is_malicious(cmd)]
то, что вы хотите сделать, это:
letters[:] = []
или
del letters[:]
Это сохранит исходный объект букв, на которые указывал
. Другие параметры, такие как letter = []
, создадут новый объект и укажут на него letter
: старый объект обычно через некоторое время будет собираться сборщиком мусора.
Причина, по которой не все значения были удалены, заключается в том, что вы меняете список во время итерации по нему.
ETA : если вы хотите отфильтровать значения из списка, вы можете использовать такие интерпретации списка:
>>> letters=['a','b','c','d','e','f','g','h','i','j','k','l']
>>> [l for l in letters if ord(l) % 2]
['a', 'c', 'e', 'g', 'i', 'k']
Вероятно, python использует указатели, и удаление начинается спереди. Переменная «письма» во второй строке частично имеет другое значение, чем переменная «буквы» в третьей строке. Когда i равно 1, тогда a удаляется, когда i равно 2, тогда b был перемещен в позицию 1, а c удаляется. Вы можете попробовать использовать «пока».
Вы не можете изменить список, который вы повторяете, иначе вы получите такой странный результат. Для этого вы должны перебрать копию списка:
for i in letters[:]:
letters.remove(i)
Он удаляет первое вхождение, а затем проверяет наличие следующего числа в последовательности. Поскольку последовательность изменилась, она принимает следующее нечетное число и так далее ...
Вы не можете перебирать список и изменять его одновременно, вместо этого перебирать срез:
letters=['a','b','c','d','e','f','g','h','i','j','k','l']
for i in letters[:]: # note the [:] creates a slice
letters.remove(i)
print letters
Тем не менее, для такой простой операции, как эта, вы должны просто использовать:
letters = []