перебирая список, удаляя элементы, некоторые элементы не удаляются - PullRequest
7 голосов
/ 30 марта 2010

Я пытаюсь перенести содержимое одного списка в другой, но он не работает, и я не знаю, почему нет. Мой код выглядит так:

list1 = [1, 2, 3, 4, 5, 6]
list2 = []

for item in list1:
    list2.append(item)
    list1.remove(item)

Но если я запустлю его, мой вывод будет выглядеть так:

>>> list1
[2, 4, 6]
>>> list2
[1, 3, 5]

У меня вопрос в три раза, я думаю: почему это происходит, как я могу заставить его работать, и я пропускаю невероятно простое решение, такое как «движение» или что-то подобное?

Ответы [ 10 ]

8 голосов
/ 30 марта 2010

Причина в том, что вы (добавляете и) удаляете из первого списка, в результате чего он становится меньше. Таким образом, итератор останавливается, прежде чем можно будет просмотреть весь список.

Чтобы достичь желаемого, сделайте следующее:

list1 = [1, 2, 3, 4, 5, 6]
list2 = []

# You couldn't just make 'list1_copy = list1',
# because this would just copy (share) the reference.
# (i.e. when you change list1_copy, list1 will also change)

# this will make a (new) copy of list1
# so you can happily iterate over it ( without anything getting lost :)
list1_copy = list1[:]

for item in list1_copy:
    list2.append(item)
    list1.remove(item)

list1[start:end:step] - это синтаксис : когда вы оставляете start пустым, по умолчанию он равен 0, когда вы оставляете end пустым, это максимально возможный значение. Итак, list1 [:] означает все в нем . (спасибо Wallacoloo)

Как говорили некоторые парни, вы также можете использовать extend -метод list -объекта, чтобы просто скопировать один список в другой, если это было вашим намерением. (Но я выбрал путь выше, потому что это близко к вашему подходу.)

Поскольку вы новичок в python, у меня есть кое-что для вас: Dive Into Python 3 - это бесплатно и просто. - Веселись!

8 голосов
/ 30 марта 2010

Вы удаляете элементы из списка1, пока выполняете итерации по нему.

Это напрашивается на неприятности.

Попробуйте это:

>>> list1 = [1,2,3,4,5,6]
>>> list2 = []
>>> list2 = list1[:] # we copy every element from list1 using a slice
>>> del list1[:] # we delete every element from list1
4 голосов
/ 30 марта 2010

Основной навык отладки: добавьте print операторов. (или print функции в Python 3)

>>> list1= [1, 2, 3, 4, 5, 6]
>>> for item in list1:
...     print item
...     list1.remove(item)
...     print list1
... 
1
[2, 3, 4, 5, 6]
3
[2, 4, 5, 6]
5
[2, 4, 6]

Обратите внимание, что Python пытается пройти через позиции списка, но вы продолжаете удалять элементы из списка, в результате чего позиции становятся бессмысленными.

Python выбирает элемент из списка в позиции 0.

Затем вы удаляете элемент, изменяя список.

Затем Python выбирает элемент в позиции 1 из списка (появляется, чтобы пропустить элемент)

Затем вы удаляете этот элемент, изменяя список.

Затем Python выбирает элемент в позиции 2 из списка (кажется, чтобы пропустить элемент)

Затем вы удаляете элемент, изменяя список.

Затем Python хотел бы выбрать элемент в позиции 3, но такого элемента нет. Так что цикл останавливается.

3 голосов
/ 30 марта 2010

Вы не должны изменять список, пока вы перебираете его. Это заставляет итератор указывать на неправильный элемент. После обработки первого элемента итератор указывает на индекс = 1, но поскольку вы удалили элемент, следующий элемент теперь имеет нулевой индекс, поэтому он будет пропущен. Вот почему вы работаете только с каждым другим предметом.

Попытка:

 list2.extend(list1) # This appends all items from list1 to list2.
 del list1[:] # From ChristopheD's post.
1 голос
/ 30 марта 2010

Как видно из других ответов, вы пытаетесь изменить список, перебирая его. Это не работает Есть много способов скопировать один список в другой. Я сделал несколько тестов, чтобы увидеть, насколько быстро каждый подход:

>>> timeit.Timer('list2 = list1[:]', 'list1 = range(10**3)').timeit(10**6)
3.9134418964385986

>>> timeit.Timer('list2 = []; list2.extend(list1)', 'list1 = range(10**3)').timeit(10**6)
4.9082601070404053

>>> timeit.Timer('list2 = copy.copy(list1)', 'import copy; list1 = range(10**3)').timeit(10**6)
7.5023419857025146

>>> timeit.Timer('list2 = [i for i in list1]', 'list1 = range(10**3)').timeit(10**6)
95.697894811630249

Синтаксис среза самый быстрый. Это намного быстрее, чем использование списка.

Чтобы очистить список, вы можете использовать:

del list1[:]
1 голос
/ 30 марта 2010

Существует также функция простого копирования (или глубокая копия, если у вас есть сложные объекты, а не только целые числа в вашем списке):

from copy import copy

list2 = copy(list1)

Вы можете получить более подходящий ответ, если объясните, чего пытаетесь достичь (если только вы не изучаете списки Python).

«Переменные» в Python - это просто имена / ссылки, поэтому деструктивная копия, которую вы, похоже, хотите сделать, кажется странной. Если вы хотите, чтобы list2 имел те же значения, что и list1, вы можете просто сделать:

list2 = list1 # now they are both referring to the same list

И если после этого вы хотите использовать list1 для чего-то другого, вы можете просто сделать:

list1 = ['A', 'B', 'C']
1 голос
/ 30 марта 2010

Попробуйте вместо этого:

list1 = [1, 2, 3, 4, 5, 6]
list2 = []

list2.extend(list1)
list1[:] = []
1 голос
/ 30 марта 2010

Использование списка понимания:

list2 = [item for item in list1]

Свяжите имя list2 с тем же объектом, что и list1:

list2 = list1

(Обратите внимание, что если вы измените содержимое list1, list2 изменится соответственно.)

Создайте копию list1 и привяжите ее к имени list2:

list2 = list1[:]

В этом случае list1 и list2 - это разные объекты.

1 голос
/ 30 марта 2010

Точно так, как говорит ChristopheD.

Может сделать это:

list1 = [1, 2, 3, 4, 5, 6]
list2 = []

for item in list1:
    list2.append(item)

list1 = []

Это очистит список1.

Редактировать Он / она обновил свой пост. Я оставлю это как небольшую альтернативу.

0 голосов
/ 30 марта 2010

@ potatocubed: Многие из приведенных ответов решают тривиальный пример, который вы дали («переместить список 1 в список 2»), но на самом деле не объясняете «почему» более глубокой проблемы, которая заключается в изменении списка во время итерации. Это. Изучите ответ С. Лотта ...

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