You are altering a mutable value, so you need to make an explicit copy:
bar = list(foo)
or
bar = foo[:]
When assigning to a name in Python, all you do is store a reference to the value. Without creating a copy of the list, both foo and bar referred to the same list.
For non-mutable values this is not a problem; you replace a reference to, say, a string to point to a different value instead.
But lists (and dict and set and instances of most classes) are mutable. You didn't change foo, you changed the value foo refers to. You did foo.append(9), which Python sees as:
- Find the value
foo refers to.
- Find the attribute
.append on that value. This returns a method on a list instance.
- Call the method, passing in the value
9. This alters the list.
Python names themselves are nothing more than labels, pointing to actual values. You could see the values as balloons, the names as little paper tags, and assignment is the act of tying the labels to the balloons. bar = foo created a second paper tag, that was tied to the exact same balloon.
See this older answer of mine where I push the balloon metaphor around some more.