Странное поведение Python с перечислением

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

Пример 1:

x = [1, 2, 3, 4, 5] for i, s in enumerate(x): del x[0] print(i, s, x) 

Пример 2:

 x = [1,2,3,4,5] for i, s in enumerate(x): x = [1] print(i, s, x) 

Пример 1 выполняется только 3 раза, потому что когда i==3 , len(x)==2 .

Пример 2 выполняется 5 раз, хотя len(x)==1 .

Итак, мой вопрос: enumerate ли вы полный список пар (index, value) в начале цикла и итерации через него? Или они генерируются на каждой итерации цикла?

6 Solutions collect form web for “Странное поведение Python с перечислением”

enumerate () возвращает итератор или другой объект, поддерживающий итерацию. Метод __next __ () итератора, возвращаемый enumerate (), возвращает кортеж, содержащий счетчик (начиная с начала, который по умолчанию равен 0), и значения, полученные от итерации по итерабельному .

__next __ () возвращает следующий элемент из контейнера. Если дополнительных элементов нет, StopIteration исключение StopIteration .

Перечисляет ли enumerate () полный список пар (индекс, значение) в начале цикла и выполняет итерацию через него? Или они генерируются на каждой итерации цикла?

Итак, enumerate() возвращает итератор и на каждой итерации __next__() проверяет, есть ли дополнительные элементы. enumerate() не создает полный список в начале цикла.

Как упоминал @Wisperwind , во втором случае вы назначаете новый объект имени x . Объект, итерация цикла не изменяется во время итерации.

В первом примере вы фактически изменяете список, который вы повторяете.

С другой стороны, во втором случае вы назначаете новый объект только для имени x . Тем не менее, объект, который выполняется итерацией цикла, не изменяется.

Посмотрите на http://foobarnbaz.com/2012/07/08/understanding-python-variables/ для более подробного объяснения имен и переменных в Python.

Просто разъяснение тому, что сказал Васи Ахмад и Виспервинд. Оба утверждают, что «вы назначаете новый объект только для имени x». Это может быть немного запутанным, поскольку это может быть истолковано как «вы создаете новый объект ( [1] ) и сохраняете его до имени x , на которое вы скажете« Ну, да, почему бы не изменить его ?! »Чтобы узнать, что происходит, распечатайте идентификатор объекта

 x = [1, 2, 3, 4, 5] y = x # To keep a reference to the original list print id(x), id(y) for i, v in enumerate(x): x = [1] print id(x), id(y) print id(x), id(y) # output (somewhat contrived as I don't have a python environment set up) # X ID Y ID 10000000000001 10000000000001 10000000000002 10000000000001 10000000000003 10000000000001 10000000000004 10000000000001 10000000000005 10000000000001 10000000000006 10000000000001 10000000000006 10000000000001 

Вы заметите, что id x меняется каждый раз через цикл, и когда вы закончите цикл, x укажет на последнюю модификацию, выполненную в цикле. Когда вы проходите через свой цикл, он выполняет итерацию по исходному экземпляру x, независимо от того, можете ли вы все еще ссылаться на него.

Как вы можете видеть, y указывает на исходный x . Когда вы выполняете свои итерации через цикл, хотя x меняется, y все еще указывает на исходный x который все еще зацикливается.

Действительно: ваш первый фрагмент изменяет список повторений на месте; второй указывает переменную x на новый список, оставляя неизмененный список, перечеркнутый enumerate() . Вы можете увидеть это в действии, перейдя на следующие ссылки на http://www.pythontutor.com, которые позволяют вам выполнить одноэтапный над вашим кодом и визуализировать содержимое ваших переменных:

  • Первая версия ( x изменена на месте).

  • Вторая версия ( x перенаправляется на [1] ).

Чтобы лучше понять, что происходит, перейдите сюда, чтобы перейти к следующему расширенному коду:

 x = [1,2,3,4,5] view = enumerate(x) for i, s in view: x = [1] print(i, s, x) 

Другие уже указали, что ваш второй пример только изменяет значение, на которое указывает x , но не список, по которому вы выполняете итерацию. Это прекрасный пример разницы между обычным назначением ( x = [1] ) и назначением среза ( x[:] = [1] ). Последний изменяет список x точек на место :

 x = [1, 2, 3, 4, 5] for i, s in enumerate(x): x[:] = [1] print(i, s, x) 

распечатает

 (0, 1, [1]) 
 x = [1, 2, 3, 4, 5] 

Список [1, 2, 3, 4, 5] отмечен символом x

 for i, s in enumerate(x): 

enumerate () прикрепляет другой тег, поэтому [1, 2, 3, 4, 5] теперь отмечены x и y . enumerate () будет продолжать использовать тэг y , а не тег x .

 del x[0] 

Список, хранящийся в памяти, изменяется, поэтому x и y теперь относятся к [2, 3, 4, 5]

Кроме того, когда вы используете

 x = [1] 

В памяти создается новый список [1] , и теперь этот тег x указывает на это. Тег y по-прежнему указывает на исходный список.

Как работает переменная Python:
http://foobarnbaz.com/2012/07/08/understanding-python-variables/

  • Замена для ... если итерация массива
  • Итерации forLoop как индекс индекса в шаблоне Django
  • Параллельный цикл в Python
  • if else statment, не соответствующий elif правильно
  • Шаблон Django не может зацикливаться по умолчанию
  • Повторение элементов в списке Python
  • элегантный способ преобразования массив numpy, содержащий datetime.timedelta в секунды в python 2.7
  • Как вводить большие данные в python pandas с использованием циклов или параллельных вычислений?
  •  
    Interesting Posts for Van-Lav
    Python - лучший язык программирования в мире.