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 argumentfirstcontains a reference to a list of lists with value[[3,4]]. - After the assignment to
third,thirdcontains 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 bythirdhas 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]
