"All slice operations return a new list containing the requested elements" This is from the python tutorials.
But if this is the case then why does this piece of code behave this way:
>>> a = [11, 3, 1999]
>>> a[:] = [9, 78]
>>> a
[9, 78]
But then I have also observed this:
>>> b = [4, 5, 6]
>>> b[:].append(5)
>>> b
[4, 5, 6]
You are confusing expressions with assignment. Getting values (reading) is handled differently from setting values (writing).
Assignment (setting) re-uses syntax to specify a target. In an assignment like a[:] = ...
, a[:]
is a target to which the assignment takes place. Using a[:]
in an expression produces a new list.
In other words: you have two different language statements, that are deliberately using the same syntax. They are still distinct however.
See the Assignment statements reference documentation:
assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression) target_list ::= target ("," target)* [","] target ::= identifier | "(" [target_list] ")" | "[" [target_list] "]" | attributeref | subscription | slicing | "*" target
[...]
- 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.
(Bold emphasis mine).
Compare this with the Slicings section in the expressions reference documentation; slicing in an expression produces a slice()
object, which the list.__getitem__
method interprets as a request for a new list object with the matching indices copied over. Other object types can choose to interpret a slice object differently.
Note that there is a third operation, the del
statement to delete references, including slices. Deletion takes the same target_list
syntax and asks to remove the indices indicated by a slice.
These three operations are, under the hood, implemented by the object.__getitem__()
(reading), object.__setitem__()
(writing) and object.__delitem__()
(deleting) hook methods; the key
argument to each of these operations is a slice()
object, but only __getitem__
is expected to return anything.