Explaining ‘[:]’ in Python
3 min readSep 27, 2022
This is part of the educational quest to finish Leetcode’s Explore Card Introduction to Data Structure Array and String. See how my quest is going at CODE QUEST > Leet Code > Introduction to Data Structure: Array and String (AAS)
The aim of this page📝 is to overcome my initial misunderstanding of the last line of the following
def rotate_arrat(nums:List[int], k:int) -> None:
n = len(nums)
a = [0] * n
for i in range(n):
a[(i + k) % n] = nums[i]
nums[:] = a <<<<<<<<<<<<<<<<
…which is the second official solution to the wonderful Rotate Array > LeetCode puzzle.
[:] SYNTAX EXPLAINED
[:]
syntax belongs to slicing feature of python list[:]
syntax selects all items of the sliced list[:]
syntax is used idiomatically. Why? Because of the Python Object Model[:]
syntax is not used that much in the wild as far as I've been reading through other people's code (wrong?)[:]
syntax is used as a list assignment technique, i.e. in combination with=
operator[:]
syntax is, confusingly perhaps, possible to use both on the left and the right side of an assignment statement
# BOTH SYNTACTICALLY VALID:
l[:] = l2
l2 = l[:]
- Without going too deep into the weeds here, I want to stress that it matters on which side of the assignment you see
[:]
- Again, I find this confusing/risky
LET’S FIRST FORGET FORGET ABOUT [:]
- Also, I am not going too deep into Python Object Model, however, you need to understand some of it for the following to make sense
- In summary, if you have say a list
l
with values [1,2,3,4] and you assign that list to another listl2
, the two names will refer to/share the same list object - You can check the object id with the built-in
id
function
>>> l = l2 = [1,2,3,4]
>>> id(l)
106046568
>>> id(l2)
106046568
>>> id(l) == id(l2)
True
- Because lists are mutable — which is of the essence here — the objects are not recreated upon modification
- Therefore, if you modify
l
, you also modifyl2
— which is extremely dangerous
>>> l
[1, 2, 3, 4]
>>> l2
[1, 2, 3, 4]
>>> l.append(5)
>>> l2
[1, 2, 3, 4, 5]
[:] ON THE RIGHT OF =
: COPY
- Therefore, if you want to copy a list you cannot just do
new_list = old_list
- We need to create a
new_list
as a new object - For this, we are utilizing the so-called colon copy with the use of slicing
- The syntax is
new_list = old_list[:]
- With the example from above, see that the lists are not identical and therefore the modification of the old list does not affect the new list
>>> old_list = [1,2,3,4]
>>> new_list = old_list[:]
>>> id(old_list)
87148616
>>> id(new_list)
99036456
>>> old_list.append(5)
>>> old_list
[1, 2, 3, 4, 5]
>>> new_list
[1, 2, 3, 4]
- Note that this is a shallow copy, meaning that the lists are referring to are still sharing elements
- This means that if you change the first element of the list in the old list, it will be changed also in the new list
- But most of the time, a shallow copy is enough
[:] ON THE LEFT OF =
: REPLACE
- this is an entirely different usecase with dangerously similar syntax
- say that you have a list
l
with values[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- with the following function nothing happens to the values of
l
outside of the function
>>> l = list(range(10))
>>> def re_assign(l):
... l2 = [1,2,3,4]
... print("old id: ", id(l))
... l = l2
... print("new id: ", id(l))
... return l
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> re_assign(l)
old id: 109195048
new id: 99638600
[1, 2, 3, 4]
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- the
[:]
on the left of an assignment does in_place replacement - this means you are keeping the ID
- this means — dangerously — that you are changing the value of
l
also outside of the function - can be dangerous for the caller!
>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> def in_place(l):
... l2 = [1,2,3,4]
... print("old id :", id(l))
... l[:] = l2
... print("still the same id:", id(l))
... return l
>>> in_place(l)
old id : 108260840
still the same id: 108260840
[1, 2, 3, 4]
>>> l
[1, 2, 3, 4]