0

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?

2 Answers2

1

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]
Carl_M
  • 861
  • 1
  • 7
  • 14
  • Sorry, pls see edited qn – Tan Yong Boon Jun 26 '22 at 14:09
  • Yes, but I am curious why that is the output, because I thought third is assigned to [3,4] in the first line of the function – Tan Yong Boon Jun 26 '22 at 14:13
  • Sorry, I've edited my question again. Pls take a look thanks so much! – Tan Yong Boon Jun 26 '22 at 15:12
  • 1
    Does this method of referencing now create a new list that third is referenced to? Yes it creates a new list. Remember you can always check in several ways besides id you can use `list1 is list2`. That will be true if they are names pointing to the same location. `list1 == list2` would not be a good test to see if they point to the same location. It would be true if they are lists stored in different locations but have exactly the same contents, – Carl_M Jun 26 '22 at 15:24
1

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 argument first 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 by first. 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 variable third.
  • 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 by first, and it is also the one referenced by third. Because the object (namely, the list) that is referenced by third has now been modified, any use of this reference to access the list (such as print(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).

constantstranger
  • 9,176
  • 2
  • 5
  • 19
  • Sorry, I've edited my qn again to reflect an additional query – Tan Yong Boon Jun 26 '22 at 15:12
  • 1
    Please see the second part of my updated answer which addresses your additional query. – constantstranger Jun 26 '22 at 15:48
  • It's interesting that slicing creates new objects while just assigning or indexing does not. – Tan Yong Boon Jun 26 '22 at 16:33
  • Focus on the concept of `mutability` or `immutability` of the data types with which you are working. integer, float are immutable. Assigning the name means assigning the name to a memory location that contains that value. Python binds/connects the integer. float to a location that contains that value. Take the `lyst = [1,2,3]` of integers. Now enter`x=1, y=2 z=3`. `id(lyst[0]) == id(x), id(lyst[1]) == id(y), id(lyst[2) == id(z)` will all be `True` because python looks for an immutable value that already exists and binds the name to it. – Carl_M Jun 26 '22 at 17:33