Перебор фрагментарной копии списка

#python

#python

Вопрос:

Я пытаюсь понять разницу между циклическим перебором списка и циклическим перебором «фрагментарной» копии списка.

Так, например, в следующем списке элемент, длина которого больше 6, добавляется в начало списка:

 words = ['cat', 'window', 'blahblah']

for word in words[:]:
    if len(word) > 6:
        words.insert(0, word)

print(words)

words = ['blahblah', 'cat', 'window', 'blahblah']
  

Затем я запускаю следующее, чтобы понять, почему это неправильный способ сделать это, но мой интерпретатор зависает, и я должен выйти. Почему это происходит? Я просто добавляю что-то в начало моего списка, что разрешено, поскольку списки изменчивы…

 for word in words:
    if len(word) > 6:
        words.insert(0, word)
  

Может кто-нибудь, пожалуйста, помочь мне понять, почему этот последний бит останавливает мою программу?

Комментарии:

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

Ответ №1:

Список words состоит из трех элементов. Копия words также выполняет. Вы выполняете итерацию по копии, вставляете что-то, words если текущий элемент длиннее 6 символов, и готово.

Теперь давайте посмотрим, что происходит, когда вы выполняете итерацию words напрямую:

Первые два шага итерации выполняются нормально, потому что условие равно False . Но поскольку len('blahblah') > 6 теперь вы вставляете 'blahblah' в начало списка. Теперь список выглядит следующим образом:

['blahblah', 'cat', 'window', 'blahblah']

Вы только что видели третий элемент, так что теперь цикл продолжается и просматривается четвертый элемент, но поскольку вы вставили что-то в начало списка, остальная часть списка сдвинулась, и снова появился новый четвертый элемент 'blahblah' . blahblah по-прежнему длиннее 6 символов, вы вставляете его снова в начале и застреваете в бесконечном цикле:

 ['cat', 'window', 'blahblah']
   ^
['cat', 'window', 'blahblah']
            ^
['cat', 'window', 'blahblah']
                       ^
['blahblah', 'cat', 'window', 'blahblah']
                                   ^
['blahblah', 'blahblah', 'cat', 'window', 'blahblah']
                                               ^
...
  

Комментарии:

1. Когда я пишу print words[:] в цикле, я вижу, что он растет — это верно и для print words also. Не понимаю, почему он не переходит в бесконечный цикл для 1-го случая.

2. Если вы пишете print words[:] в цикле, он каждый раз создает новую копию, но копия, которую вы повторяете, не меняется. Вы можете увидеть это, написав copy = words[:] перед циклом, выполнив итерацию copy и распечатав copy внутри цикла.

Ответ №2:

В вашем первом подходе вы выполняете мелкую копию своего списка words , перебираете его и добавляете в список длинные слова words . Таким образом, вы выполняете итерацию по фиксированному списку и расширяете другой список.

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

Ответ №3:

Когда вы выполняете words[:] , вы выполняете итерацию по копии list , тогда как с words помощью, вы выполняете итерацию по исходной копии list .

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

Где, поскольку это не так words[:] , поскольку список, в который вы добавляете, и тот, в котором вы выполняете итерацию, отличаются.

Ответ №4:

Следующий цикл

 for word in words:
    // do something
  

примерно эквивалентно следующему (когда words это список, а не другой вид iterable ):

 i = 0
while i != len(words):
    word = words[i]
    // do something (assuming no 'continue' here)
    i  = 1
  

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

Ответ №5:

words[:] означает, что новая копия words и длина фиксирована. Итак, вы выполняете итерацию по фиксированному списку.

Но во второй ситуации повторение и расширение того же списка, что делает его бесконечным.

 words = ['cat', 'window', 'blahblah']
new_words = [_ for _ in words if len(_) > 6]
print(new_words)
  

Комментарии:

1. Неправильный ответ, потому что пользователь не спрашивает, как он должен это делать, а почему метод, которому он следует, терпит неудачу.

Ответ №6:

В случае, если вы хотите увидеть, что именно происходит на фоне вставки в список (слова) против копии фрагмента (слова[:]), вы можете изменить / табулировать печать, чтобы увидеть бесконечный цикл, поскольку бесконечный цикл не может быть виден в его исходном местоположении в скрипте:

 for w in words:  
if len(w) > 6:
    words.insert(0, w) 
print(words) #prints after each insert, let you see the infinite loop