Why is reverse() on specific slice of a list not working?

Viewed 127

I have a list, a = [0,1,2,3,4,5].

I'm trying to reverse the slice a[1:4], with a[1:4].reverse(). However, the value of a does not change. Why does this happen?

Note: I don't want to know how to make this work - rather, why this happens.

5 Answers

a[1:4] creates a new list. You called reverse on this new list, not a

Here's what a[1:4].reverse() actually does:

  • create a sublist (slice) of a, so a[1:4] == [1, 2, 3]
  • reverse that list in-place, so it's now [3, 2, 1]
  • throw that list away because you didn't ask it to do anything with it

This might be easiest to see with a little monitor wrapper (subclass) of list:

class ListWrapper(list):
    def __setitem__(self, key, value):
        print(f'__setitem__ was called on {id(self)}')
        super(ListWrapper, self).__setitem__(key, value)

    def __getitem__(self, item):
        print(f'Get Item was called on {id(self)}')
        n = ListWrapper(super(ListWrapper, self).__getitem__(item))
        print(f'New id is {id(n)}')
        return n

    def reverse(self):
        print(f'Reversed Called on {id(self)}')
        super(ListWrapper, self).reverse()

Essentially the ListWrapper incorporates a few print statements among the methods used in this operation:

a = ListWrapper([0, 1, 2, 3, 4, 5])
print('ID of `a` is', id(a))
a[1:4].reverse()
print('ID of `a` is', id(a))

The output of which is:

ID of `a` is 2002062741728
__getitem__ was called on 2002062741728
New id is 2002062745088  # Notice the ID is different
reverse called on 2002062745088  # Reverse is called on this new object
ID of `a` is 2002062741728  # The id of `a` has not changed

We have no way to access the value with id 2002062745088 since we don't have a variable anywhere which contains this information.


In response to one of the questions in the comments: "If this is the case, then why does a[1:4] = [3,2,1] change the value of a?"

Let's also look at that operation:

a = ListWrapper([0, 1, 2, 3, 4, 5])
print('ID of `a` is', id(a))
a[1:4] = [3, 2, 1]
print('ID of `a` is', id(a))

The output of which is:

ID of `a` is 2760760102160
__setitem__ was called on 2760760102160
ID of `a` is 2760760102160

Notice that a has the same id throughout. And neither of the functions in the previous example were called (__getitem__ and reverse). As such, the update happened to the list a and it's results are accessible.

list.reverse() is an in-place function that modifies the original list.

When you call a[1:4], a[1:4] is only a value that got sliced from a, and no container, i.e. a variable, is holding the variable.

The in-place modifying function is modifying the newly created list, that is, the returned sliced list. However, since there is no container holding it, nor does it point back to the original a, it will be lost.

Reference documentation here

You are actually working on a[1:4] and not a. Since you didn't reference it, the list is thrown away.

See:

>>> a[1:4].reverse()
#==== No output ====
>>> d=a[1:4]
>>> d.reverse()
[3, 2, 1]

When the list was not referenced, it was discarded. But when you assigned it to a variable d, you can see the result.

Related