One of the major use case when a developer needs to take mutability into account is when passing arguments to a function. This is very important, because this will determine the ability for the function to modify objects that doesn't belong to its scope, or in other words if the function has side effects. This is also important to understand where the result of a function has to be made available.
>>> def list_add3(lin):
lin += [3]
return lin
>>> a = [1, 2, 3]
>>> b = list_add3(a)
>>> b
[1, 2, 3, 3]
>>> a
[1, 2, 3, 3]
Here, the mistake is to think that lin
, as a parameter to the function, can be modified locally. Instead, lin
and a
reference the same object. As this object is mutable, the modification is done in-place, which means that the object referenced by both lin
and a
is modified. lin
doesn't really need to be returned, because we already have a reference to this object in the form of a
. a
and b
end referencing the same object.
This doesn't go the same for tuples.
>>> def tuple_add3(tin):
tin += (3,)
return tin
>>> a = (1, 2, 3)
>>> b = tuple_add3(a)
>>> b
(1, 2, 3, 3)
>>> a
(1, 2, 3)
At the beginning of the function, tin
and a
reference the same object. But this is an immutable object. So when the function tries to modify it, tin
receive a new object with the modification, while a
keeps a reference to the original object. In this case, returning tin
is mandatory, or the new object would be lost.
>>> def yoda(prologue, sentence):
sentence.reverse()
prologue += " ".join(sentence)
return prologue
>>> focused = ["You must", "stay focused"]
>>> saying = "Yoda said: "
>>> yoda_sentence = yoda(saying, focused)
Note: reverse
operates in-place.
What do you think of this function? Does it have side effects? Is the return necessary? After the call, what is the value of saying
? Of focused
? What happens if the function is called again with the same parameters?