I want to understand why:
a = []
;del a
; anddel a[:]
;behave so differently.
I
To understand the difference between different ways of deleting lists, let us see each of them one by one with the help of images.
>>> a1 = [1,2,3]
A new list object is created and assigned to a1
.
>>> a2 = a1
We assign a1
to a2
. So, list a2
now points to the list object to which a1
points to.
DIFFERENT METHODS EXPLAINED BELOW:
Method-1 Using []
:
>>> a1 = []
On assigning an empty list to a1
, there is no effect on a2
. a2
still refers to the same list object but a1
now refers to an empty list.
Method-2 Using del [:]
>>> del a1[:]
This deletes all the contents of the list object which a1
was pointing to. a1
now points to an empty list. Since a2
was also referring to the same list object, it also becomes an empty list.
Method-3 Using del a1
>>> del a1
>>> a1
NameError: name 'a1' is not defined
This deletes the variable a1
from the scope. Here, just the variable a1
is removed, the original list is still present in the memory. a2
still points to that original list which a1
used to point to. If we now try to access a1
, we will get a NameError
.
Of your three "ways of deleting Python lists", only one actually alters the original list object; the other two only affect the name.
a = []
creates a new list object, and assigns it to the name a
.del a
deletes the name, not the object it refers to.del a[:]
deletes all references from the list referenced by the name a
(although, similarly, it doesn't directly affect the objects that were referenced from the list).It's probably worth reading this article on Python names and values to better understand what's going on here.
Test 1:
rebinds a
to a new object, b
still holds a reference to the original object, a
is just a name by rebinding a
to a new object does not change the original object that b
points to.
Test 2:
you del the name a
so it no longer exists but again you still have a reference to the object in memory with b
.
Test 3
a[:]
just like when you copy a list or want to change all the elements of a list refers to references to the objects stored in the list not the name a
. b
gets cleared also as again it is a reference to a
so changes to the content of a
will effect b
.
The behaviour is documented:
There is a way to remove an item from a list given its index instead of its value: the
del
statement. This differs from thepop()
method which returns a value. Thedel
statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:>>> >>> a = [-1, 1, 66.25, 333, 333, 1234.5] >>> del a[0] >>> a [1, 66.25, 333, 333, 1234.5] >>> del a[2:4] >>> a [1, 66.25, 1234.5] >>> del a[:] >>> a []
del
can also be used to delete entire variables:>>> >>> del a
Referencing the name
a
hereafter is an error (at least until another value is assigned to it). We'll find other uses fordel
later.
So only del a
actually deletes a
, a = []
rebinds a to a new object and del a[:]
clears a
. In your second test if b
did not hold a reference to the object it would be garbage collected.
del a
is removing the variable a
from the scope. Quoting from python docs:
Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block.
del a[:]
is simply removing the contents of a
, since the deletion is passed to the a
object, instead of applied to it. Again from the docs:
Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object).
.
>>> a = [1,2,3] # set a to point to a list [1, 2, 3]
>>> b = a # set b to what a is currently pointing at
>>> a = [] # now you set a to point to an empty list
# Step 1: A --> [1 2 3]
# Step 2: A --> [1 2 3] <-- B
# Step 3: A --> [ ] [1 2 3] <-- B
# at this point a points to a new empty list
# whereas b points to the original list of a
>>> a = [1,2,3] # set a to point to a list [1, 2, 3]
>>> b = a # set b to what a is currently pointing at
>>> del a # delete the reference from a to the list
# Step 1: A --> [1 2 3]
# Step 2: A --> [1 2 3] <-- B
# Step 3: [1 2 3] <-- B
# so a no longer exists because the reference
# was destroyed but b is not affected because
# b still points to the original list
>>> a = [1,2,3] # set a to point to a list [1, 2, 3]
>>> b = a # set b to what a is currently pointing at
>>> del a[:] # delete the contents of the original
# Step 1: A --> [1 2 3]
# Step 2: A --> [1 2 3] <-- B
# Step 2: A --> [ ] <-- B
# both a and b are empty because they were pointing
# to the same list whose elements were just removed
Of those three methods, only the third method actually results in deleting the list that 'a' points to. Lets do a quick overview.
When you right a = [1, 2, 3]
it creates a list in memory, with the items [1, 2, 3] and then gets 'a' to point to it. When you write b = a
this preforms whats' called a 'shallow copy,' i.e. it makes 'b' point to the same block of memory as 'a.' a deep copy would involve copying the contents of the list into a new block of memory, then pointing to that.
now, when you write a = []
you are creating a new list with no items in it, and getting 'a' to point to it. the original list still exists, and 'b' is pointing to it.
in the second case, del a
deletes the pointer to [1,2,3] and not the array it's self. this means b
can still point to it.
lastly, del a[:]
goes through the data 'a' is pointing to and empties it's contents. 'a' still exists, so you can use it. 'b' also exists, but it points to the same empty list 'a' does, which is why it gives the same output.