Почему Python пропускает элементы, когда я изменяю список во время итерации по нему? - PullRequest
10 голосов
/ 13 апреля 2009

Я сейчас занимаюсь разработкой программы на python и только что заметил, что что-то не так с циклом foreach в языке или структурой списка. Я просто приведу общий пример моей проблемы для упрощения, поскольку я получаю одинаковое ошибочное поведение как в моей программе, так и в общем примере:

x = [1,2,2,2,2]

for i in x:
    x.remove(i)

print x        

Ну, проблема здесь проста, хотя я предполагал, что этот код должен был удалить все элементы из списка. Ну, проблема в том, что после его выполнения я всегда получаю 2 оставшихся элемента в списке.

Что я делаю не так? Заранее всем спасибо за помощь.

Редактировать: я не хочу очищать список, это всего лишь пример ...

Ответы [ 6 ]

31 голосов
/ 13 апреля 2009

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

for i in x[:]:
    x.remove(i)

[:] возвращает «ломтик» x, который содержит все его элементы и, таким образом, фактически является копией x.

11 голосов
/ 13 апреля 2009

Когда вы удаляете элемент, и цикл for включает следующий индекс, вы затем пропускаете элемент.

Сделай это задом наперед. Или, пожалуйста, укажите вашу реальную проблему.

4 голосов
/ 13 апреля 2009

Я думаю, в широком смысле, что когда вы пишете:

for x in lst:
    # loop body goes here

под капотом python делает что-то вроде этого:

i = 0
while i < len(lst):
    x = lst[i]
    # loop body goes here
    i += 1

Если вы вставите lst.remove(x) для тела цикла, возможно, тогда вы сможете понять, почему вы получаете результат, который вы делаете?

По сути, python использует движущийся указатель для перемещения по списку. Указатель начинается с указания на первый элемент. Затем вы удаляете первый элемент, тем самым делая элемент second новым первым элементом. Затем указатель перемещается на новый второй - ранее третий - элемент. И так далее. (может быть понятнее, если вы используете [1,2,3,4,5] вместо [1,2,2,2,2] в качестве списка образцов)

3 голосов
/ 13 апреля 2009

Почему бы вам просто не использовать:

x = []

Вероятно, это потому, что вы изменяете тот же массив, что итерируете.

Попробуйте ответ Криса-Джестера Янга, если хотите очистить массив по-своему.

2 голосов
/ 06 августа 2016

Я знаю, что это старый пост с принятым ответом, но для тех, кто еще может прийти ...

В нескольких предыдущих ответах указывалось, что менять итерацию во время итерации - плохая идея. Но как способ подчеркнуть, что происходит ...

>>> x=[1,2,3,4,5]
>>> for i in x:
...     print i, x.index(i)
...     x.remove(i)
...     print x
...
1 0
[2, 3, 4, 5]
3 1
[2, 4, 5]
5 2
[2, 4]

Надеюсь, что визуальное помогает прояснить.

1 голос
/ 07 ноября 2009

Я согласен с Джоном Фухи в отношении условия разрыва. Обход копии списка работает для метода remove (), как предложил Крис Джестер-Янг. Но если нужно использовать pop () определенных элементов, то, как упоминал Эрик, итерация в обратном порядке работает, и в этом случае операция может быть выполнена на месте. Например:

def r_enumerate(iterable):
    """enumerator for reverse iteration of an iterable"""
    enum = enumerate(reversed(iterable))
    last = len(iterable)-1
    return ((last - i, x) for i,x in enum)

x = [1,2,3,4,5]
y = []
for i,v in r_enumerate(x):
    if v != 3:
        y.append(x.pop(i))
    print 'i=%d, v=%d, x=%s, y=%s' %(i,v,x,y)


или с помощью xrange:

x = [1,2,3,4,5]
y = []
for i in xrange(len(x)-1,-1,-1):
    if x[i] != 3:
        y.append(x.pop(i))
    print 'i=%d, x=%s, y=%s' %(i,x,y)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...