This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
How can I initialize a list of lists to a certain size?
(2 answers)
Closed 6 years ago.
I have been using the [Value] * Count notation in Python for initializing a list. For eg., [False] * 3 results in creation of list [False, False False]. I tried using the same notation for initializing a list of lists.
>>>a = [[0] * 2] * 3
>>>print a
[[0, 0], [0, 0], [0, 0]]
>>>a[0][1] = 23
>>>print a
[[0, 23], [0, 23], [0, 23]]
>>>id(a[0])
139978350226680
>>>id(a[1])
139978350226680
>>>id(a[2])
139978350226680
As we can see, the elements of a refer to a single list thrice instead of referring to three different lists.
1. Why does this happen?
2. What is the correct way to initialize a list of lists?
The same behavior has been pointed out in an answer previously: https://stackoverflow.com/a/13382804/4716199
That's the behavior of the * operator on a list: it does not make copies, but copies the reference. That makes sense since it is in general not even defined how to make copies, so Python can only copy the reference since it is the only thing every object has.
In case you want to construct new lists, you can use list comprehension:
a = [[0] * 2 for _ in range(3)]
In general it is not good practice to use * given the list contains mutable objects.
For immutable objects like bool and str this works since you do not work on the object itself, but on the resulting array. In you example you used chained indexing, so you modify the inner list.
Related
This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 months ago.
>>> m=[[-1]*2]*2
>>> n=[[-1,-1],[-1,-1]]
>>> m==n
True
>>> for i in range(2):
... m[i][i]=10
...
>>> m
[[10, 10], [10, 10]]
>>> for i in range(2):
... n[i][i]=10
...
>>> n
[[10, -1], [-1, 10]]
In the code block above, the assignment to the elements of n takes place as expected, but the assignment to elements of m is incorrect although both m and n before the assignment are equal, and the assignment takes place in the same manner. Can someone please clarify? Is this a bug in the usage of the * operator for the creation of the original list? This is Python 3.10.0.
Lists are used to store separate values, you can't declare a integer into a list and attempt to multiply it without specifying which number to multiply, and putting a integer in brackets m=[[-1]*2]*2 is how that can work, instead, do m=[-1] m[0]*2*2
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Removing from a list while iterating over it [duplicate]
(5 answers)
Closed 10 months ago.
A simplified scenario that's not working as expected. I'm hoping someone can point out why.
nums = [0, 0, 4, 0]
new_list = nums
for i in range(len(nums)):
if nums[i] !=0:
break
del new_list[0]
new_list
I'm trying to remove preceding zeroes. I expect the above loop to delete the first element of the list until it encounters a non-zero number, then break. It's only deleting the first zero, even though my del new_list[0] statement is in the loop.
Expected output: [4, 0]
Actual output: [0, 4, 0]
The assignment operator in python (as used in new_list = nums) does not create a new object but just assigns another name to the existing object. Any changes you make to nums will also affect new_list.
To create a new object, you can make a shallow copy instead:
new_list = nums.copy()
In python, assignment operator doesn't make you a new instance of your object, in your casenums. I suggest you to read about deep copy and shallow copy in python. As this article says
In Python, Assignment statements do not copy objects, they create bindings between a target and an object. When we use = operator user thinks that this creates a new object; well, it doesn’t. It only creates a new variable that shares the reference of the original object. Sometimes a user wants to work with mutable objects, in order to do that user looks for a way to create “real copies” or “clones” of these objects. Or, sometimes a user wants copies that user can modify without automatically modifying the original at the same time, in order to do that we create copies of objects.
You can make a deep copy using the copy module.
import copy
nums = [0, 0, 4, 0]
new_list = copy.deepcopy(nums) # using deepcopy for deepcopy
nums = [0, 0, 4, 0]
new_list = nums # this refer to nums and when you change new_list, nums changes
# do following to make a copy.
new_list = nums[::]
for i in range(len(nums)):
if nums[i] !=0:
break
del new_list[0]
new_list
Output
[4, 0]
This question already has answers here:
Creating a new list from elements of an other list, referencing the elements of the latter
(2 answers)
Closed 2 years ago.
Why the output here is [[2, 1], [2, 1]] and not [[2,1],[1,1]]
a = [[1] * 2] * 2
a[0][0]=2
print (a)
I have done quite research on the * operator in list but it doesn't seem to get me to the correct understanding
It's due to the fact, how Python handles objects in memory. When you are using the * 2 to duplicate the list, Python does not create a separate list object in memory, it only copies the memory pointer for the second list position. Due to this behaviour when you mutate the first element, it affects the second too.
You can use the id() function to check the object ids within Python. You will see that a[0] and a[1] will have the same ids.
>>> a = [[1] * 2] * 2
>>> a[0][0] = 2
>>> a
[[2, 1], [2, 1]]
>>> id(a[0])
140608840898432
>>> id(a[1])
140608840898432
>>> id(a[0]) == id(a[1])
True
When you do this, you are just creating copies of list, n times.
So the inner lists here are actually the same list. When you modify the first one, you're also modifying the second. If you don't want that functionality, you can do this instead:
a = [[1 for _ in range(2)] for _ in range(2)]
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
I have come across a strange behaviour in Python's value assignment. If I create a list such as List =[[0, 0], [0, 0]] and want to change a value indexed 1 in the list indexed as 1 I simply use: List[1][1] = 1. However, this is not a case of the code below. Eventhough the list seems same as the one mentioned above, it assigns more values then I expect. Is there anyone who can explain how this work?
size = 2
matrix = [[]]
for j in range(size):
matrix[0].append(0)
for i in range(size-1):
matrix.append(matrix[0])
matrix[1][1] = 1
print(str(matrix))
I would expect the output as [[0, 0], [0, 1]], but the actual output is [[0, 1], [0, 1]]. Why is the value at position matrix[0][1] assigned as 1??
When you do:
matrix.append(matrix[0])
You're appending to matrix the reference to the object matrix[0] (which is a list), it isn't creating a new list.
Im trying to make a nested list work but the problem is that whenever i append a variable it is the same as the first one.
array = [[1,0]]
index = 1
for stuff in list:
array.insert(0,array[0])
array[0][0]+=1
index += 1
if index == 5:
break
print(array)
This returns [[5, 0], [5, 0], [5, 0], [5, 0], [5, 0]]
The weird thing is if i were to make the list into a int it would work.
array = [1]
index = 1
for stuff in array:
array.insert(0,array[0])
array[0]+=1
index += 1
if index == 5:
break
print(array)
This one returns [5, 4, 3, 2, 1]
For the program i am writing i need to remember two numbers. Should i just give up on making it a list or should i make it into two ints or even a tuple? Also is it even possible to do with lists?
I changed list into array same concept though
That is because of this line
list.insert(list[0])
This always refers the list[0] and refereed in all the inserts which you did in the for loop.
And list of integers and list of lists, behave differently.
Also, mention your expected output.
Just to follow up with an explanation from the docs:
Assignment statements in Python do not copy objects, they create
bindings between a target and an object.
So whenever you want to copy objects that contain other objects, like your list contains integers or how a class may contain other members, you should know about this difference (also from the docs):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in
the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
To achieve a deep copy, as you tried to do in this situation, you can either use Python's built-in list copy method, if you're on Python 3.3 or higher:
deepcopy = list.copy()
Or use the copy module for lower Python versions, which includes a copy.deepcopy() function that returns a deep-copy of a list (or any other compound object).