Подтвердить что ты не робот

Странное поведение 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) в начале цикла и перебирает его? Или они генерируются на каждой итерации цикла?

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

Просто разъяснение тому, что сказал Васи Ахмад и Виспервинд. Оба утверждают, что "вы назначаете новый объект только для имени 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 of x меняется каждый раз через цикл, и когда вы закончите цикл, x укажет на последнюю модификацию, выполненную в цикле. Когда вы проходите через свой цикл, он выполняет итерацию по исходному экземпляру x, независимо от того, можете ли вы все еще ссылаться на него.

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

Ответ 4

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

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

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

Ответ 5

Другие уже указали, что ваш второй пример изменяет значение, на которое 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])

Ответ 6

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/