I came across the following code (sort of):
my_list = [1, [2, 3, 4], 5]
my_list[1:2] = my_list[1]
After running these two lines, the variable my_list
will be [1, 2, 3, 4, 5]
. Pretty useful for expanding nested lists.
But why does it actually do what it does?
I would have assumed that the statement my_list[1:2] = my_list[1]
would do one of the following:
[2, 3, 4]
into the second position in the list (where it already is)my_list[1:2]
). (Repeating the above with a Numpy array instead of a list results in a similar error.)Other questions (e.g. How assignment works with python list slice) tend to not pay much attention to the discrepancy between the size of the slice to be replaced, and the size of the items you're replacing it with. (Let alone explaining why it works the way it does.)
No, slicing returns a list which is inside the original list. Not a copied list.
Slice assignment is a special syntax for lists, where you can insert, delete, or replace contents from a list: Insertion: >>> a = [1, 2, 3] >>> a[0:0] = [-3, -2, -1, 0] >>> a [-3, -2, -1, 0, 1, 2, 3] Deletion: >>> a [-3, -2, -1, 0, 1, 2, 3] >>> a[2:4] = [] >>> a [-3, -2, 1, 2, 3]
In short, slicing is a flexible tool to build new lists out of an existing list. Python supports slice notation for any sequential data type like lists, strings, tuples, bytes, bytearrays, and ranges. Also, any new data structure can add its support as well.
“Indexing” means referring to an element of an iterable by its position within the iterable. “Slicing” means getting a subset of elements from an iterable based on their indices.
Slice assignment replaces the specified part of the list with the iterable on the right-hand side, which may have a different length than the slice. Taking the question at face value, the reason why this is so is because it's convenient.
You are not really assigning to the slice, i.e. Python doesn't produce a slice object that contains the specified values from the list and then changes these values. One reason that wouldn't work is that slicing returns a new list, so this operation wouldn't change the original list.
Also see this question, which emphasizes that slicing and slice assignment are totally different.
Here is the relevant bit from the Python Language Reference
If the target is a slicing: The primary expression in the reference is evaluated. It should yield a mutable sequence object (such as a list). The assigned object should be a sequence object of the same type. Next, the lower and upper bound expressions are evaluated, insofar they are present; defaults are zero and the sequence’s length. The bounds should evaluate to integers. If either bound is negative, the sequence’s length is added to it. The resulting bounds are clipped to lie between zero and the sequence’s length, inclusive. Finally, the sequence object is asked to replace the slice with the items of the assigned sequence. The length of the slice may be different from the length of the assigned sequence, thus changing the length of the target sequence, if the target sequence allows it.
This behavior makes sense qualitatively because when you slice a list you get a sub list so replacing that with another list shouldn't add a level of nesting. Allowing it to change the length of the list is a design choice. Other choices are possible as your numpy
example demonstrates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With