Странное поведение Python с enumerate
Я знаю, что не должен изменять список внутри цикла, но просто из любопытства я хотел бы знать, почему количество итераций отличается между следующими двумя примерами.
Пример 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 ответов:
Перечислять() возвращает итератор или какой-либо другой объект, поддерживающий итерацию. Тот самый __следующий__() метод итератора, возвращаемого перечислять() возвращает кортеж, содержащий число (от начала, которое по умолчанию равно 0) и значения, полученные при итерации по iterable.
__следующий__() возвращает следующий элемент из контейнера. Если других элементов нет, вызовите исключение
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()
. Вы можете увидеть это в действии, перейдя по следующим ссылкам 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/