Bob
Bob

Reputation: 35

enumerate() for sliced list does not work as expected

enumerate() for a list works as expected.

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)

In this example, num shows swapped value as expected.

0 0
1 1
2 33 <--
3 22 <-- 
4 4

But if I change mylist to mylist[:-1] like:

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 does not show swapped value.

0 0
1 1
2 22 <--
3 33 <--

Why does this happen?

Upvotes: 1

Views: 559

Answers (3)

Austin
Austin

Reputation: 26039

mylist[:-1] is creating a new list object but you perform swap using mylist.

How do you know it's a different object?

You can test this behaviour using id():

>>> mylist = [0,1,22,33,4,5]
>>> id(mylist)
3954492760
>>> lst = mylist
>>> id(lst)
3954492760
>>> sliced = mylist[:-1]
>>> id(sliced)
3954492920

If you observe, sliced's id is different.


Now coming to your code:

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.

The easiest fix you can do is slice beforehand:

sliced = mylist[:-1]
for pos, num in enumerate(sliced):
    print(pos,num)
    if pos == 0:
        swap(sliced,2,3)

So as others mentioned this has nothing to do with enumerate.

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1123470

The difference is that you are enumerating on a copy of the list. In your first example, you are enumerating on a direct reference to the list that you are altering, but in the second example, you used slicing on the list to enumerate over. Slicing returns a new list object.

This is important because you are altering what not-yet-iterated-over positions are referencing to. List iteration uses an ever-increasing index to yield values, altering the list as you iterate can mean that a next index references a different value.

That the iteration is done via the enumerate() function doesn't really matter. You'd have the same problem without enumerate():

for num in mylist:
    print(num)
    if num == 0:
        swap(mylist, 2, 3)

would print

0
1
33
22
4

and slicing would give you a copy, so:

for num in mylist[:-1]:
    print(num)
    if num == 0:
        swap(mylist, 2, 3)

outputs

0
1
22
33

If the goal is to skip the last element, then instead of creating a copy through slicing, you could use the enumerated position to break out of the loop early:

for pos, num in enumerate(mylist):
    if pos == len(mylist) - 1:
        break
    print(pos, num)
    if pos == 0:
        swap(mylist, 2, 3)

or you can use the itertools.islice() function

from itertools import islice

for pos, num in enumerate(islice(mylist, len(mylist) - 1)):
    print(pos, num)
    if pos == 0:
        swap(mylist, 2, 3)

or you could simply create your copy first, then alter the indices of that copy:

sliced = mylist[:-1]
for pos, num in enumerate(sliced):
    print(pos, num)
    if pos == 0:
        swap(sliced, 2, 3)

Upvotes: 1

mistiru
mistiru

Reputation: 3376

The problem doesn't come from the enumerate function.

Actually, when doing mylist[:-1] you are creating a new list, and working on it.

So when you try to modify your list (swap(mylist,2,3)), you modify the original one, not the new one (created by mylist[:-1]).

You can solve your problem by doing:

new_list = mylist[:-1]
for pos, num in enumerate(new_list):
    print(pos,num)
    if pos == 0:
        swap(new_list,2,3)

Upvotes: 2

Related Questions