Итерирование и обновление списка в python

Я не могу понять, почему следующий код отправляется в неопределенный цикл (когда я не использую список копий)

list = ["Mohit","kumar","sffsfshfsd"] for w in list: if(len(w)) > 5: list.insert(0,w) print("inside loop") print(list) 

Выше код печатает внутри цикла на неопределенный срок.

Теперь, если вместо списка, я использую список копий, как показано ниже, отлично работает.

 list = ["mohit","kumar","sffffgssddf"] for w in list[:]: if len(w) > 5: list.insert(0,w) print("inside loop") print(list) 

Теперь я прочитал в документации на python, что это поведение, которое я получу, но я хочу понять причину этого. Заранее спасибо.

6 Solutions collect form web for “Итерирование и обновление списка в python”

Первый цикл for w in list будет использовать итератор (из iter(list) ) для извлечения и циклического перемещения по каждому элементу в списке. Этот итератор не получает весь список сразу – он ленив , то есть он получает только один элемент за раз из списка, когда это необходимо. Здесь вы можете узнать об итерационном протоколе , или итерации / генераторах и лень здесь .

Циклирование через индексы 0 и 1 ничего не делает, поскольку их длины строк меньше 6. При индексе 2, однако, вы добавляете "sffsfshfsd" в начало list . Теперь list вырос, и есть что-то в индексе 3: "sffsfshfsd" . Затем итерация продолжается, выбирая значение из следующего индекса (3), которое добавляется в начале снова, перемещая то же значение, которое было в индексе 3, чтобы индексировать 4 … Цикл никогда не заканчивается.

Во втором цикле w in list[:] вы создаете копию всего списка ( с помощью оператора среза ) и итерации по нему. Вы добавляете элементы в исходный список, а не в копию, поэтому итератор не будет трогать элементы, которые вы добавили.

PS: Я попытался найти исходный код Python (который есть C), чтобы доказать, что итераторы списков фактически используют инкрементирующий индекс (как описано выше). Я не очень разбираюсь в чтении исходного кода Python, но вот что я нашел в cpython / listobject.c :

Создание итератора, устанавливает начальный индекс на 0

 2797 static PyObject * 2798 list_iter(PyObject *seq) 2799 { .... 2806 it = PyObject_GC_New(listiterobject, &PyListIter_Type); .... 2809 it->it_index = 0; .... 2813 return (PyObject *)it; 2814 } 

next использует it->it_index сверху, а затем увеличивает его

 2831 static PyObject * 2832 listiter_next(listiterobject *it) 2833 { .... 2844 item = PyList_GET_ITEM(seq, it->it_index); 2845 ++it->it_index; .... 2847 return item; .... 2853 } 

Кажется законным для меня?

Чтобы моделировать, как итерация списков работает внутри, давайте перепишем вашу программу с использованием целочисленных индексов и цикла while.

 lst = ["Mohit", "kumar", "sffsfshfsd"] pos = 0 while pos < len(lst): word = lst[pos] print('lst=%s pos=%d word=%s' % (lst, pos, word)) if len(word) > 5: lst.insert(0, word) pos += 1 

Ниже показано, что происходит при запуске:

 lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=0 word=Mohit lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=1 word=kumar lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=2 word=sffsfshfsd lst=['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=3 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=4 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=5 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=6 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=7 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=8 word=sffsfshfsd lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=9 word=sffsfshfsd ... 

(Это продолжается до тех пор, пока вы не исчерпаете ни ОЗУ, ни терпения.)

Как вы можете видеть, вы продолжаете 'sffsfshfsd' окончательный 'sffsfshfsd' вправо, поэтому ваш код продолжает смотреть на него и никогда не останавливается.

Это не происходит, если вы работаете над копией, так как вы больше не изменяете список, который вы повторяете.

Это также не произойдет, если вы должны либо отрегулировать индекс цикла после вставки:

  if len(word) > 5: lst.insert(0, word) pos += 1 # account for the extra word pos += 1 

или переместите это слово вместо копирования:

  if len(word) > 5: lst.insert(0, lst.pop(pos)) # don't change len(lst) 

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

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

Цитирование из документов :

Примечание . Существует тонкость, когда последовательность изменяется контуром (это может произойти только для изменяемых последовательностей, т. Е. Списков). Внутренний счетчик используется для отслеживания того, какой элемент используется далее, и это увеличивается на каждой итерации. Когда этот счетчик достигнет длины последовательности, цикл завершается. Это означает, что если пакет удаляет текущий (или предыдущий) элемент из последовательности, следующий элемент будет пропущен (поскольку он получает индекс текущего элемента, который уже был обработан). Аналогично, если пакет вставляет элемент в последовательность перед текущим элементом, текущий элемент будет обрабатываться снова в следующий раз через цикл. Это может привести к неприятным ошибкам, которых можно избежать, сделав временную копию, используя фрагмент всей последовательности, например,

 for x in a[:]: if x < 0: a.remove(x) 

За цикл по списку в Python поддерживает счетчик внутри, и он используется для получения следующего элемента.

В вашем первом коде, когда он достигает sffsfshfsd (т. sffsfshfsd Индекс 2), вы снова вставляете его в начало списка, поэтому все элементы сдвигаются на одно место, и теперь sffsfshfsd будет перенесен в индекс 3 и будет выбран на следующей итерации. И это продолжается …

Во втором коде вы повторяете копию списка, и копия списка не изменяется при изменении исходного списка.

 lst = ["Mohit","kumar","sffsfshfsd"] for i, w in enumerate(lst): print("Index: {i} | List: {list}".format(i=i, list=lst)) if(len(w)) > 5: lst.insert(0, w) 

Выходы:

 Index: 0 | List: ['Mohit', 'kumar', 'sffsfshfsd'] Index: 1 | List: ['Mohit', 'kumar', 'sffsfshfsd'] Index: 2 | List: ['Mohit', 'kumar', 'sffsfshfsd'] Index: 3 | List: ['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] Index: 4 | List: ['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] Index: 5 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] Index: 6 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] 

Я думаю, что это очень интересный вопрос. Я считаю, что ответ должен присутствовать в реализации исходного кода на языке python (извините, что я не смог его найти и надеюсь, что кто-то специалист может направить нас на реализацию Python)

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

с другой стороны [:], этот оператор создаст новую копию исходного набора данных. Таким образом, независимо от того, как вы меняете исходный набор данных, цикл for циклически копирует (который не изменяется).

Доказательство:

 list = ["mohit","kumar","sffffgssddf"] test = list list.append("test") print test #['mohit', 'kumar', 'sffffgssddf', 'test'] #clear data, let's try [:] list = ["mohit","kumar","sffffgssddf"] test = list[:] list.append("test") print test #['mohit', 'kumar', 'sffffgssddf'] 

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

Надеюсь, поможет.

  • iteritems в Python
  • как установить pycairo для python 3 на Ubuntu 10.04
  • Как установить атрибут класса с ожиданием в __init__
  • Как создать модальный диалог в tkinter?
  • Эффективная многопроцессорность массивной максимизации грубой силы в Python 3
  • python писать в файл из словаря
  • Как проверить, какая деталь сложного объекта не может быть маринована
  • Почему быстрее ломаться, чем возбуждать исключение?
  •  
    Interesting Posts for Van-Lav

    Как передать свой идентификатор и пароль на сайт в Python с помощью Google App Engine?

    Объединить два ряда в искровом свете на основе состояния в pyspark

    Как расширить класс в python?

    Форматирование вывода в виде таблицы

    Выполнение пользовательской вкладки в python argparse

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

    python Decimal – проверка, если целое число

    Python, как убивать потоки, заблокированные в очереди с помощью сигналов?

    Создание объекта JSON, сериализуемого с помощью обычного кодировщика

    Динамическое наследование Python: как выбрать базовый класс при создании экземпляра?

    Функции тестирования модулей, которые обеспечивают доступ к файлам

    Насколько близки разработки веб-серверов к веб-серверам производства?

    Python cProfile: как отфильтровать конкретные вызовы из профилирующих данных?

    Django возвращает ошибку 403 при отправке запроса POST

    Как создать новый (постоянный) профиль Firefox с Selenium в Python?

    Python - лучший язык программирования в мире.