Some objects in python (like list
s, dict
s and set
s) are mutable, whereas others (like tuple
s, frozenset
s, string
s and numbers) are immutable.
Operations on mutable objects modify the object themselves. Many immutable objects also allow operations, which in this case return a new object. (Internally this is implemented with the dunder "magic methods", which in this case just return a new object. x OPERATOR y
actually calls a method on x(and/or y).)
Since in python x = y
binds the name x
to point to y
, if y
is mutable and you modify it, x
will point to the modified y
. But if y
is immutable (but allows operations), operations on y
will create a new y
, to which x
no longer points:
x = [] # mutable
y = x # y and x both point to a particular '[]'
x += ["hi"]
y == ["hi"] # True
x = 0
y = x # y and x both point to 0
x += 7 # x now points to 7
y == 7 # False
Exactly the same thing goes on when you parse an argument to a function---you bind a name in the function's scope pointing to that object. What then happens to the object pointed to by the name depends upon the type of object and what you do to it. If it's mutable and you mutate the object pointed to by the name in the function, you will mutate the object itself, and any other names bound to that object ouside the function will see the changed object.
This is the reason that we don't generally do this:
def f(x=[]):
...
Since f
is evaluated once, when the module is first loaded, x
will point to the same list for the duration of the function. Thus if the function does anything to x
, x
will be in that state next time it's called (which is rarely what you want).