These two cases are more different than they seem, and it isn't because of the types involved.
x = 3
This changes what value x
is looking at. Reassigning x
using =
does not change the 3. It wouldn't matter if the right hand side were a int, or a list. Whatever is on the right is put into the variable on the left.
x[0] = 'd'
This looks the same, but it's actually quite different. Instead of a simple reassignment, this is actually internally translated to
x.__setitem__(0, 'd')
Instead of being just a reassignment, this is actually a mutative operation that changes the object x
is holding.
So the reason the latter acts differently is that instead of changing what object x
is holding, you're altering the object itself that x
is holding.
You can tell the two cases apart based on what's on the left of =
. If the left is a simple variable name (x =
), it's a plain reassignment. If the left side uses []
(x[0] =
) or anything other kind of "accessor" syntax like a .
(x.attr =
), you're altering the object that x
is holding.