Assignment is always just rebinding names in Python, which means you end up with aliases for the same object all over the place. Any time you give a name to any kind of object (you assign an existing object a new name, or you receive it passed into a function, or you pull it out of some container), anything you do to actually alter this object will affect it wherever it originally was (i.e. the caller who passed it into your function, or anybody else looking at the container you pulled it out of).
You can make your code explicitly take copies of things, if you need it to; list slicing with mylist[:]
is probably the way you're most likely to be familiar with. Many built in operations do this; in particular it's usually a safe assumption with builtin functions/methods that if they return an object they haven't modified the originals (and this is a very good rule to follow most of the time; if your function or method has its effect by altering existing objects, it should return None
and let the callers just look at the objects they gave you). In fact particularly for lists there are lots of pairs of methods/functions that do the same thing; usually there's a function that returns a new modified copy of the list and a method that returns nothing but alters the list. e.g. compare sorted_list = sorted(mylist)
vs mylist.sort()
, reversed_list = reversed(mylist)
vs mylist.reverse()
, etc.
But in the case when copying is going on, you do need to be careful about how deep the copy goes; in most cases it's only to the outer-most level, so mutating anything contained inside the copy will be visible from the original object.
New Python programmers need to get a handle on this as early as possible, as it pervades every single aspect of programming in Python.
Unfortunately, this issue is obscured by the most natural staring point for new programmers. You start by figuring out how to do basic manipulations of numbers and text strings, but those are immutable in Python. That doesn't mean they don't work "the same way" as lists, it just means there are no operations you can do that will cause them to change. So you don't need to care about whether they're being shared, because it won't make a difference even if they are.
Other answers have explained in more detail exactly what's going on in your example. But whenever you modify a list (or any other object), you need to think "where did this list come from?". Because most of the time, unless it's a new list that's just been created, other parts of your program will be able to see the changes you make. There is nearly always aliasing going on with list manipulations. Much of the time it doesn't matter, especially for lists of numbers or text. But you need to be aware of it all the time, so that you can decide whether or not it matters.
It does become second nature fairly easily though, so persevere and it'll get a lot easier. Having said that, even very experienced Python programmers still get caught by the occasional aliasing bug!
f
, and then set some variable to equalf
(i.e.x = f
). I can then callf
by usingx()
. That sure sounds like aliasing to me. If I understand correctly, using the assignmentx = f
makesx
refers tof
, it doesn't create an instance of f? – Phytof
is created atdef
time.x = f
just points another name at f. – Belorussiaf
doesn't affectx
after the fact. – Roush