Search code examples
pythonpython-3.xenumerate

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?


Solution

  • 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)