When I write this code:
polly = "alive"
palin = ["parrot", polly]
polly = "dead"
Python variables hold references to values. Thus, when you define the
palin list, you pass in the value referenced by
polly, not the variable itself.
You should imagine values as balloons, with variables being threads tied to those balloons.
"alive" is a balloon,
polly is just a thread to that balloon, and the
palin list has a different thread tied to that same balloon. In python, a list is simply a series of threads, all numbered starting at 0.
What you do next is tie the
polly string to a new balloon
"dead", but the list is still holding on to the old thread tied to the
You can replace that thread to
"alive" held by the list by reassigning the list by index to refer to each thread; in your example that's thread
>>> palin = polly >>> palin ['parrot', 'dead']
Here I simply tied the
palin thread to the same thing
polly is tied to, whatever that might be.
Note that any collection in python, such as
tuple, etc. are simply collections of threads too. Some of these can have their threads swapped out for different threads, such as lists and dicts, and that's what makes something in python "mutable".
Strings on the other hand, are not mutable. Once you define a string like
"alive", it's one balloon. You can tie it down with a thread (a variable, a list, or whatever), but you cannot replace letters inside of it. You can only tie that thread to a completely new string.
Most things in python can act like balloons. Integers, strings, lists, functions, instances, classes, all can be tied down to a variable, or tied into a container.
You may want to read Ned Batchelder's treatise on Python names too.