The following is a simple function:
def func(first):
third = first[0]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
I was expecting the output to be [3,4] but instead, it's supposed to be [5,4]. I thought that during the assignment of the variable "third" in the first line, the right-hand side of "first[0]" had already been evaluated to give [3,4] and thus, third would stay as this value for the rest of the function execution steps.
Edit:
I now understand why the above function works the way it does. The variable third
references the list within first
and thus, when that changes, third
will also change. I now have one more question:
What about referencing a slice of a list, instead of a specific index?
def func(first):
third = first[0][0:2]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
Does this method of referencing now create a new list that third
is referenced to?
CodePudding user response:
Your code reads:
def func(first):
third = first[0]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
What's happening is this:
- In
func()
, the argumentfirst
contains a reference to a list of lists with value[[3,4]]
. - After the assignment to
third
,third
contains a reference to the list[3,4]
at position 0 in the list referenced byfirst
. No new list object has been created and no copy of a list has taken place, rather a new reference to the existing list has been created and stored in the variablethird
. - In the line
first[0][0] = 5
, the item at position 0 in the list[3,4]
is updated so that the list is now[5,4]
. Note that the list[3,4]
that was modified is an element of the list of lists referenced byfirst
, and it is also the one referenced bythird
. Because the object (namely, the list) that is referenced bythird
has now been modified, any use of this reference to access the list (such asprint(third)
) will reflect its updated value, which is[5,4]
.
UPDATE:
The code for your updated question is:
def func(first):
third = first[0][0:2]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
In this case, the assignment third = first[0][0:2]
takes a slice of the list [3,4]
at position 0 in the list of lists referenced by first
. Taking a slice in this way creates a new object which is a copy of the subsequence indicated by the arguments specified in the square brackets, so after the assignment, the variable third
contains a reference to a newly created list with value [3,4]
. The subsequent assignment first[0][0] = 5
updates the value of the list [3,4]
in position 0 of the list of lists referenced by first
, with the result that the value of the list becomes [5,4]
, and has no effect on the value of third
which is an independent object with value [3,4]
.
Importantly (and potentially confusingly), slice notation used on the left-hand side of an assignment works very differently. For example, first[0][0:2] = [5,4]
would change the contents of the list first[0]
such that the elements in index 0 and 1 are replaced by [5,4]
(which in this case means the value of the list object would be changed from [3,4]
to [5,4]
, but it would be the same object).
CodePudding user response:
A variable identifies a memory location. If the contents of the memory location is mutable like a list, the contents are changed by code. All names that identify that location will reflect the change. If the contents are immutable like an integer the name will be assigned a new memory location. x = 5
y =5
, x
and y
point to the same memory location. x = 6
y
will still point to the location containing 5
. x
will point to the location containing 6
.
def func(first):
third = first[0]
print(f'{id(first[0])=} {id(third)=}') # They are the same list.
# Name points to same address. So changing contents of memory.
first[0][0] = 5
print(third)
first = [[3, 4]]
func(first)
Output
id(first[0])=2652914318144 id(third)=2652914318144
[5, 4]