enumerate() для списка работает как положено.
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
mylist = [0,1,22,33,4]
for pos, num in enumerate(mylist):
print(pos,num)
if pos == 0:
swap(mylist,2,3)
В этом примере num
показывает измененное значение, как и ожидалось.
0 0
1 1
2 33 <--
3 22 <--
4 4
Но если я изменю mylist
на mylist[:-1]
, например:
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
mylist = [0,1,22,33,4]
for pos, num in enumerate(mylist[:-1]):
print(pos,num)
if pos == 0:
swap(mylist,2,3)
num
не показывает измененное значение.
0 0
1 1
2 22 <--
3 33 <--
Почему это происходит?
Проблема не в функции enumerate
.
На самом деле, делая mylist[:-1]
, вы создаете список новый и работаете над ним.
Поэтому, когда вы пытаетесь изменить свой список (swap(mylist,2,3)
), вы изменяете исходный, а не новый (созданный mylist[:-1]
).
Вы можете решить свою проблему, выполнив следующие действия:
new_list = mylist[:-1]
for pos, num in enumerate(new_list):
print(pos,num)
if pos == 0:
swap(new_list,2,3)
Разница в том, что вы перечисляете копировать списка. В вашем первом примере вы перечисляете прямую ссылку на список, который вы изменяете, но во втором примере вы использовали нарезку в списке для перечисления. Нарезка возвращает новый объект списка.
Это важно, потому что вы изменяете то, на что ссылаются еще не повторенные позиции. Итерация списка использует постоянно увеличивающийся показатель для получения значений, изменение списка во время итерации может означать, что следующий индекс ссылается на другое значение.
То, что итерация выполняется с помощью функции enumerate()
, на самом деле не имеет значения. У вас была бы та же проблема без enumerate()
:
for num in mylist:
print(num)
if num == 0:
swap(mylist, 2, 3)
напечатал бы
0
1
33
22
4
и нарезка даст вам копию, поэтому:
for num in mylist[:-1]:
print(num)
if num == 0:
swap(mylist, 2, 3)
выходы
0
1
22
33
Если цель состоит в том, чтобы пропустить последний элемент, то вместо создания копии путем нарезки вы можете использовать пронумерованную позицию для раннего выхода из цикла:
for pos, num in enumerate(mylist):
if pos == len(mylist) - 1:
break
print(pos, num)
if pos == 0:
swap(mylist, 2, 3)
или вы можете использовать itertools.islice()
функция
from itertools import islice
for pos, num in enumerate(islice(mylist, len(mylist) - 1)):
print(pos, num)
if pos == 0:
swap(mylist, 2, 3)
или вы можете просто создать свою копию первый, а затем изменить индексы этой копии:
sliced = mylist[:-1]
for pos, num in enumerate(sliced):
print(pos, num)
if pos == 0:
swap(sliced, 2, 3)
mylist[:-1]
создает новый объект списка, но вы выполняете обмен, используя mylist
.
Откуда вы знаете, что это другой объект?
Вы можете проверить это поведение, используя id()
:
>>> mylist = [0,1,22,33,4,5]
>>> id(mylist)
3954492760
>>> lst = mylist
>>> id(lst)
3954492760
>>> sliced = mylist[:-1]
>>> id(sliced)
3954492920
Если вы заметили, идентификатор sliced
отличается.
for pos, num in enumerate(mylist[:-1]):
print(pos,num)
if pos == 0:
swap(mylist,2,3)
# ^^^ - you used a different list object from what you were iterating over.
Исправление самый простой, которое вы можете сделать, это заранее нарезать:
sliced = mylist[:-1]
for pos, num in enumerate(sliced):
print(pos,num)
if pos == 0:
swap(sliced,2,3)
Итак, как уже упоминалось, это не имеет ничего общего с enumerate
.