Let's break down your comments 1 by 1:
1.) x[:2] = [6, 7]
slices can be modified:
See these answers here. It's calling the __setitem__
method from the list
object and assigning the slice
to it. Each time you reference x[:2]
a new slice object is created (you can simple do id(x[:2])
and it's apparent, not once will it be the same id).
2.) indices of slices cannot be modified:
That's not true. It couldn't be modified because you're performing the assignment on the slice
instance, not the list
, so it doesn't trigger the __setitem__
to be performed on the list
. Also, int
are immutable so it cannot be changed either way.
3.) slices of slices cannot be modified:
See above. Same reason - you are assigning to an instance of the slice and not modifying the list
directly.
4.) this version of a for-loop cannot modify lists:
z
being referenced here is the actual objects in the elements of x
. If you ran the for loop with id(z) you'll note that they're identical to id(6), id(7), id(3), id(4), id(5)
. Even though list
contains all 5 identical references, when you do z = ...
you are only assigning the new value to the object z
, not the object that is stored in list
. If you want to modify the list
, you'll need to assign it by index, for the same reason you can't expect 1 = 6
will turn x
into [6, 2, 3, 4, 5]
.
5.) this version of a for-loop can modify lists:
See my answer above. Now you are directly performing item assignment on the list
instead of its representation.
6.) if I assign a slice to a var, it can be modified:
If you've been following so far, you'll realize now you are assigning the instance of x[:2]
to the object y
, which is now a list
. The story follows - you perform an item assignment by index on y
, of course it will be updated.
7.) ...but it has no impact on the original list:
Of course. x
and y
are two different objects. id(x) != id(y)
, therefore any operation performed on x
will not affect y
whatsoever. if you however assigned y = x
and then made a change to y
, then yes, x
will be affected as well.
To expand a bit on for z in x:
, say you have a class foo()
and assigned two instances of such to the list f
:
f1 = foo()
f2 = foo()
f = [f1, f2]
f
# [<__main__.foo at 0x15f4b898>, <__main__.foo at 0x15f4d3c8>]
Note that the reference in question is the actual foo
instance, not the object f1
and f2
. So even if I did the following:
f1 = 'hello'
f
# [<__main__.foo at 0x15f4b898>, <__main__.foo at 0x15f4d3c8>]
f
still remains unchanged since the foo instances remains the same even though object f1
now is assigned to a different value. For the same reason, whenever you make changes to z
in for z in x:
, you are only affecting the object z
, but nothing in the list is changed until you update x
by index.
If however the object have attribute or is mutable, you can directly update the referenced object in the loop:
x = ['foo']
y = ['foo']
lst = [x,y]
lst
# [['foo'], ['foo']]
for z in lst:
z.append('bar')
lst
# [['foo', 'bar'], ['foo', 'bar']]
x.append('something')
lst
# [['foo', 'bar', 'something'], ['foo', 'bar']]
That is because you are directly updating the object in reference instead of assigning to object z
. If you however assigned x
or y
to a new object, lst
will not be affected.