Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
behavior list of lists
#1
Dear all,

I am new to Python, and currently I am practicing around a bit with lists. One of the assignments is to create a 3D list. Below is the code I use to create this 3D lists. I consists of some for-loops that create a list of lists. The output after the first print statement looks valid. If I request the value of a single element of the list, say lst[1][1][1], then it gives me a single value of 0 which is what I would expect. However, when I assign the entry lst[1][1][1] a different value, lst[1][1][1] = 4, it not only updates the element in lst[1][1][1], but it updates multiple entries of the list. I would expect that it only assigns the entry lst[1][1][1] the value 4, while leaving the other entries unaffected. What am I missing here?

PS. I know that there are more elegant methods to create a 3D list (list comprehension), but I am more interested in the behavior of lists than an elegant solution.

# define variables for dimensions
a = 3
b = 5
c = 8
 
# create two lists with zeros
lst_dim2 = [0]*b
lst_dim3 = [0]*c

# create 3D list
lst = [0]*a
for i in range(a):
    lst[i] = lst_dim2 
    for j in range(b):
        lst[i][j] = lst_dim3

# print the list
print(lst)

# assign value to element of list
lst[1][1][1] = 4

# print list after element assignment
print(lst)
Reply
#2
You have defined lists and then added the same lists to another list multiple times.

The following adds the same list to another list twice and the output shows that they are the same lists.
a = 3
lst_dim2 = [0]*a

lst = [lst_dim2, lst_dim2]
print(lst[0] is lst[1])
Output:
True
The following creates new lists that are added to another list and the output shows that they are different lists.
a = 3

lst = [[0]*a, [0]*a]
print(lst[0] is lst[1])
Output:
False
Reply
#3
Hi Yoriz,

thanks for the reply. Oke, I see. So in your first example (which corresponds to my case), the values are changed in multiple places because the lists are the same, right?

Now if I consider the following example:

# define variable
a = 0

# create 1D list
lst = [0]*10
for i in range(10):
    lst[i] = a
 
# print the list
print(lst)
 
# assign value to element of list
lst[1] = 4
 
# print list after element assignment
print(lst)
I would expect that changing lst[1] results in a change of all entries in that list, because in this case lst[0] == lst[1] == lst[2] etc.. Why does it not update the entire list in this case?
Reply
#4
In this example you do not have the same list inside another list, you only have one list and one pointer to the list, the other indexes are indexes of the list itself, they are not separate attributes pointing to the same list.
To see the same behaviour as before you would have to have another attribute pointing to the same list.
I have added lst_pointer2 to the below copy of your above code.
# define variable
a = 0
 
# create 1D list
lst = [0]*10
lst_pointer2 = lst
for i in range(10):
    lst[i] = a
  
# print the list
print(lst)
  
# assign value to element of list
lst[1] = 4
  
# print list after element assignment
print(lst)
print(lst_pointer2)
Output:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 4, 0, 0, 0, 0, 0, 0, 0, 0] [0, 4, 0, 0, 0, 0, 0, 0, 0, 0]
Reply
#5
This is a mutable vs immutable thing. Lists, dictionaries and sets are mutable. They can be changed, modified, mutated. Numbers, strings and tuples are immutable. You cannot make 5 = 6 or "this" = "that".

When making a list of lists you ran into trouble because you used the same mutable list over and over. Because it is mutable, setting lst[5] = 6 modified the list instead of creating a new list. All variables referencing the list see the modification too.

In "a = 0", zero is immutable. You cannot call "0 = 4" to change the value of zero. Similarly you cannot change the value of zero by calling "lst[1] = 4". This replaces the zero in lst[1] with a four. lst[2] still references zero.

For a different reason, changing the assignment of "a" does not change the values in lst. After you initialize lst using "lst[i] = a", lst is full of zeros, not full a's. There is no relationship between lst and a.

And finally, I think you are confusing variables with the objects the variables reference. In C, a variable is a memory location. When you declare a variable "int a = 0", this allocates enough memory to hold an int, associates this memory with a variable named a, and fills the memory with 0. The variable "a" will always reference the same memory location and the only values that can be assigned to "a" must fit in this memory slot and be of type int. In Python I suppose variables are still allocated memory, but I have no idea how to find where that memory is. In Python it is best to think of variables as purely being names. In Python, all things are objects. A list is an object. A string is an object. 4 is an object. When you call "a = 0" you are not copying the value of zero into some memory associated with the variable "a", instead you assign "a" to reference the object 0. In the next line of the program you could assign a to reference a string or a list or a function. "a" is only a convenient name that your program can use to reference some object.

When you execute this code:
a = 0
lst = [0]*10
lst_pointer2 = lst
for i in range(10):
    lst[i] = a
You are not filling lst with "a". Remember, "a" is just a name and it has no representation of it's own. There is no way to put "a" in "lst" because "a" doesn't exist. So what "lst[i] = a" does is get the object "a" references and place that in lst[i]. You can see this here:
a = 0
lst = [a]
print(id(0), id(a), id(lst[0]))
Output:
140721012463232 140721012463232 140721012463232
id(obj) gets the identifier for obj. The identifier is what Python uses to get (and possibly set) the value of an object. 0 is not really the number zero. It is an object, and the value of the object is zero.
If I modify lst[0] = 4, you will see that lst now references a different object.
a = 0
lst = [a]
print(id(0), id(4), id(lst[0]))
lst[0] = 4
print(id(0), id(4), id(lst[0]))
Output:
140721012463232 140721012463360 140721012463232 140721012463232 140721012463360 140721012463360
The first print shows that lst[0] references object 0. The second print shows lst[0] references object 4.
Reply
#6
Thanks for the explanation deanhystad, my confusion was indeed related to the immutable and mutable objects. Since a list always holds the same memory adress, even after its elements are changed, I was under the impression that a variable also always holds the same memory adress (like in C++). However, after your explanation and reading this website: https://realpython.com/pointers-in-python/, I understand that variables and lists are different. It is now clear to me what the difference is between the two examples I showed.

Thanks all for replying to my post.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  sorting a list of lists by an element leapcfm 3 191 Sep-10-2021, 03:33 PM
Last Post: leapcfm
  List of lists - merge sublists with common elements medatib531 1 795 May-09-2021, 07:49 AM
Last Post: Gribouillis
  Sort List of Lists by Column Nju 1 610 Apr-13-2021, 11:59 PM
Last Post: bowlofred
  Creating list of lists from generator object t4keheart 1 747 Nov-13-2020, 04:59 AM
Last Post: perfringo
  How do i make a new lists out of an list ozezn1 1 548 Oct-28-2020, 10:19 PM
Last Post: Gribouillis
  List index out of range error while accessing 2 lists in python K11 2 746 Sep-29-2020, 05:24 AM
Last Post: K11
  list indices must be integers or slices, not lists error djwilson0495 2 1,035 Aug-27-2020, 06:13 PM
Last Post: deanhystad
  How to put the items of one list in new generated lists Bobbear 1 673 Jun-12-2020, 06:08 AM
Last Post: buran
  Compare Two Lists and Replace Items In a List by Index nagymusic 2 1,274 May-10-2020, 05:28 AM
Last Post: deanhystad
  Add items from one list to list of lists PUP280 5 1,408 May-05-2020, 03:47 PM
Last Post: PUP280

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020