0:06
In this video, we're going to look at a bunch of different situations where you have
two different references that appear to refer to the same list object.
And hopefully by understanding how objects and references actually work,
you'll begin to see what the different situations are,
why they behave the way they do.
So let's take a look at some of these confusing situations.
The first situation I want to take a look at is when
we have two list objects that are Look Alikes,
that is, there are two different objects but they look the same.
They have the same contents in the list. So you can see here.
I have lst1 and lst2,
two references that are held in variables,
that refer to two different but identical looking list objects.
Each of them holds the item seven, and three and two.
And so when I initially print them here,
we expect them to look the same.
And so it's going be difficult to decide,
are these actually the same object or are they just two look alikes?
So to figure it out we're going to actually mutate the object referred to by
lst1 by changing the item at index one and then print them out again.
Let's see what happens here.
Okay. So you can see that when we do that
the first list object is actually mutated through that lst1 reference,
but the second list object remains the same.
And so when we printed out through
the lst2 reference we see that it still has its original value.
We can see that these were in fact
two different objects even though they looked the same initially.
Now let's look at another situation,
the situation where we have aliases.
Okay, and here again,
when I do this first print it's going to look like
both lst3 and lst4 refer to the same object.
Now in fact this time around there is
only one list object that contains the elements one, five and nine.
And when I have the assignment here,
list four equals list three or lst4 equals lst3.
What's actually happening is we're copying the reference and now
both lst3 and lst4 refer to exactly the same list object.
They are aliases for each other.
Now we're going to mutate the object referred to by lst3 by changing
the item at index one to be 17 and let's see what happens in this situation.
All right, now we can see that there was only a single list object.
And when I print them out using the two different references,
lst3 and lst4, both of them look like they've changed.
All right, so I changed the list through one reference.
I see that change from both references.
Next, let's take a look at the situation where we copy a list object.
This time I'm going to start with a single list object that contains the items eight,
nine and four and I'm going to assign it to the variable lst5.
So now I have a reference in lst5 to this list object.
I next want to make a copy.
Well, how do I do that?
One way is to call the list function.
When I call the list function,
it makes a copy of the list object and gives me a reference back to this copy.
Right, so lst6 is going to refer to a copy of the object referred to by lst5.
Now, the list function only makes a shallow copy,
it only copies the list object itself.
It does not follow the references within
the lists to make copies of the objects that those references refer to.
This might be a little bit complicated,
but here when we have numbers,
it looks like it copied the whole thing.
But if you have lists that contain
the lists it might not work exactly as you expect, all right.
But, for our purposes here,
the list function makes a copy of that list object.
And so now we have two list objects,
but when we print them out,
they look the same.
So maybe we have two objects.
Maybe you should believe me, maybe you shouldn't.
So let's mutate the original list object through the lst5 reference by
changing the item that is contained at index one to be minus two.
And then let's print these out, right.
You'll notice the change only took place in
the first list object that was referred to by lst5,
lst6 actually refers to a different list object,
so was not mutated.
Students are often confused by what happens when they call a function and
they pass a list or some other immutable object as an argument.
So let's take a look and see what actually happens here.
I've defined a function called mutate list.
So there is no mystery to what it's going to do.
It takes a list and appends the number 42 to it and that's all it does.
It doesn't return anything.
Okay, it just appends a value to the list that's passed into it.
So you might think, oh that's going to have
no effect outside of this function, but let's see.
Okay, so we create a list object that contains the items one, two, three.
We assign it to list seven or lst7,
so now we have a reference to it.
Let's print it out and we'll see that it's the list one, two, three.
Then we call mutate lists on lst7 and let's print it again.
And let's see what happens.
The list object that lst7 refers to was actually mutated.
So, what's happening here when you call the function mutate list,
you're passing a reference to a list object.
So when you mutate it inside basically we now have aliases to the same list object.
Lst7 outside the function and alist inside
the function are references to the exact same list object.
They're aliases, so if you do something to a list inside the function you will be able to
see it through the lst7 reference outside the function.
Because of this, I very strongly recommend that
you do not write functions that mutate their parameters.
When you do this,
their effects are visible outside of the function and often have unintended consequences.
Now, sometimes you're writing a function whose sole purpose
or one of its purposes is to actually mutate the inputs.
In that case it's okay,
but it needs to be very
clearly specified in the docstring that this is what's going to happen.
So someone who calls the function understands that the object they passed to it,
is going to potentially,
at least, be changed by the function.
If you don't do that this leads to lots of subtle and insidious bugs and
a lot of confusion on the part of beginning programmers.
Now you've seen several different scenarios in which you have multiple lists references
that may or may not refer to the same underlying list object.
And we saw one possible way in which you could find yourself in each of these scenarios,
where you have lookalikes, aliases or copies.
And hopefully by seeing how they behave in the different scenarios,
you have a better understanding of how your program is going to
work when you have references to list.
In particular, you need to be very careful when you mutate a list through one reference.
Because if you have other references to that same list object,
you're going to see those mutations and
you need to make sure that's how you want your program to behave.
Perhaps most importantly, we've also seen what
happens when you pass a list to a function.
So you need to think about what you're doing and be very
careful inside your functions when you are
operating on a parameter that was a list or any mutable object for that matter.
But in particular for now when you have a list,
make sure that you only mutate that list if that's what you intended to do.
And that is what the function is supposed to do and it's
clearly identified in the docstring that that's what's going to happen.