List index changes multiple elements - python

I couldn't find anything matching my problem, so hopefully this wasn't already mentioned somewhere and I'm just too stupid to find it.
thelist = []
a = [0]
for i in range(5):
thelist.append(a)
print(thelist)
At this point, the program returns [[0], [0], [0], [0], [0]]
thelist[0].append(1)
print(thelist)
After appending this I would expect it to return the same but with the first element modified, like this:
[[0, 1], [0], [0], [0], [0]]
What actually happens, is that every element was modified in the same way and I get the following.
[[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
I have found out, that if I replace the a in line 2 with the value of a, everything works fine. But why does this not work when I append a variable multiple times??

When creating thelist, it's saving a reference to the same object (a) in each position. When you append to the first element, it's actually appending to a in all the "thelist" slots. What you need to do is a deep copy on the object during construction:
thelist = []
a = [0]
for i in range(5):
thelist.append(list(a))
print(thelist)
Then the following append will work as desired.

a is an object that every item in the list is pointing to.
If you want every item in the list to be a different object (with the same value), you should create a new object before assigning it. the easiest way would be to do:
for i in range(5):
thelist.append(list(a))
The list function receives a list and return a new instance with the same inner values. So you won't be changing the entire array every time.

thelist and a both are lists. So, inside the for loop you are appending a list into another list
Case 1: a is a list
thelist = []
a = [0]
for i in range(5):
thelist.append(a)
print(thelist)
[[0], [0], [0], [0], [0]]
Case 2: a is not a list but variable
thelist = []
a = 0
for i in range(5):
thelist.append(a)
print(thelist)
[0, 0, 0, 0, 0]

Related

Mysterious duplicate arrays in Python

I am trying to understand why the first for loop does not return what the second for loop is returning. Can anyone explain this clearly to me? My searching did not unearth any results since I don't know what this problem is called.
arr = [1,2,3]
tempArr = []
res = []
for num in range(0,3):
tempArr.append(arr[num])
res.append(tempArr)
print(tempArr, res)
print()
tempArr = []
res = []
for num in range(0,3):
tempArr.append(arr[num])
res.append(list(tempArr))
print(tempArr, res)
Returns:
[1] [[1]]
[1, 2] [[1, 2], [1, 2]]
[1, 2, 3] [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
[1] [[1]]
[1, 2] [[1], [1, 2]]
[1, 2, 3] [[1], [1, 2], [1, 2, 3]]
Lists in Python are mutable sequences. This means that you can modify the elements of a list once it is created.
You are creating a list and assigning it to the variable 'tempArr'. Then in the first loop you are appending 'tempArr' to the 'res' list.
Note that because lists are mutable, you are never changing what 'tempArr' is, you only modify its content, so you are adding three times the same list to 'res'. When you modify the 'tempArr' list, you modify all the lists inside 'res' (again, because they are the same list)
An explicit example you can play with is the following:
# Create a new list
tempArr = []
# Create a list containing the initial one (which is empty)
res = [tempArr]
# Now, lets modify the 'tempArr'
tempArr.append("This wasn't here before")
# Make your prediction on what will happen now:
print(res)
Another thing we can do is play with the 'is' comparison, in your code:
arr = [1,2,3]
tempArr = []
res = []
for num in range(0,3):
tempArr.append(arr[num])
res.append(tempArr)
# Check if the element we have appended into 'res' is the 'tempArr' list. The result is always True.
print(tempArr is res[-1])
print(tempArr, res)
# Let's compare the three elements of the 'res' list and check if they are the same list, we see that they are.
print(res[0] is res[1] is res[2])
On the other hand, in the second loop, you are not appending the 'tempArr' list into 'res', you are first creating a new list by calling "list(tempArr)", and then appending this new list into 'res'. Now we can play a little bit with this:
tempArr = []
res = []
for num in range(0,3):
tempArr.append(arr[num])
res.append(list(tempArr))
# Check if the element we have appended into 'res' is the 'tempArr' list.
print(tempArr is res[-1])
# This time the result is False. So we are not appending 'tempArr', but a new list with the same elements.
print(tempArr, res)
# Let's compare again the three elements of the 'res' list and see if they are the same elements:
print(res[0] is res[1] is res[2])
As a consequence, because in the second loop we are creating new lists, when you modify the initial one, this doesn't affect the elements inside the 'res' list.
list(...) makes a copy. Without making a copy, the same reference is appended, thus subsequent changes show up later in earlier elements as well.
To see the references, do a print(list(map(id, res))). If the printed number is the same, then it's the same list object.
Because we use list() to make shallow copy
This means list(tempArr) will now be a new and independent object with the same contents as tempArr

getting empty list even though appending while using recursion

I am trying to print all subsequences of the list "l" in the code but here i want the answer to be stored in "proc" list.But when executing the below code i am getting empty lists of list.I am practising recursion problems.I stuck to understand why i am getting empty lists of list which is "proc" list
proc = [] #global array
def fun(t,lst,i,n):
if(i==n):
print(t)
proc.append(t) #appending to the global array
return
t.append(lst[i])
fun(t,lst,i+1,n)
t.pop()
fun(t,lst,i+1,n)
arr = [6,4,2]
n = len(arr)
l = []
for i in range(n):
for j in range(i+1,n):
l.append([i+1,j+1])
fun([],l,0,len(l))
print(proc) #printing the global list proc after executing function```
I want to know why i am getting output as empty lists even though i am appending "t" list to the "proc" list
[[1, 2], [1, 3], [2, 3]]
[[1, 2], [1, 3]]
[[1, 2], [2, 3]]
[[1, 2]]
[[1, 3], [2, 3]]
[[1, 3]]
[[2, 3]]
[]
[[], [], [], [], [], [], [], []]
i want answer to be appended list of t
You have fallen prey to one of the classic Python errors. You are passing [] into the function as t. You then modify t, and pass t into the recursive call. That means there is ONLY ONE LIST t. When you do proc.append(t), you are not grabbing a snapshot of the array. You are appending multiple references to that one list. By the time the function ends, t is empty, so your proc has multiple references to an empty list.
The short term fix is to change to
proc.append( t.copy() )
or
proc.append( t[:] )

Issue while appending item in list in a list in python

I think similar questions exist but I can't seem to find them out
x=list(([],)*3)
x[0].append(1)
x[1].append(2)
x[2].append(3)
print(x)
I want the result to look like this:
[[1],[2],[3]]
But instead, I get:
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
Also I really don't want to use any loops but if it is necessary then I'll try
Just realized you don't want to use a for loop.
Option 1: no for loops:
x = []
x.append([1])
x.append([2])
x.append([3])
print(x)
> [[1], [2], [3]]
Option 2: using loops.
Easiest and cleanest way is to use list comprehension like this:
x = [[i] for i in range(1,4)]
print(x)
> [[1], [2], [3]]
Or you can use a plain for loop and create an empty list before:
result = []
for i in range(1,4):
result.append([i])
result
Instead of the first line, use
x = [[] for _ in range(3)]
In the current code, x is a list of the empty lists which are actually the same object. That's how [...] * n works; it repeats the same objects n times. So appending an item to one of these empty lists would append the item to the others, because they are the same.
You can create an empty list first and starts appending each element as a list.
x=[]
x.append([1])
x.append([2])
x.append([3])
print(x)

issue with iterating in a nested list

i am a super beginner in python (took an introductory to python course 4 years ago!) and I am trying to append the numbers 0-3 each in a list inside a list, so:
[[0], [1], [2], [3]]
i have written the code below which should work but it doesn't and i can't figure out why:
l = 4
data = [[None]] * l
for k in range(l):
data[k].append(k)
print(data)
which produces this in the shell (don't worry about the None element in there I know how to get rid of those):
[[None, 0, 1, 2, 3], [None, 0, 1, 2, 3], [None, 0, 1, 2, 3], [None, 0, 1, 2, 3]]
i have been working on this for over an hour but i can't figure out what is wrong and how to fix it. any halp would be highly appriciated
Your data list consists of four references to the same inner list, which is initialized to [None]. As you append to this inner list in your loop, all four references to it are affected.
If you create a new list for each element, you won't have this issue:
l = 4
data = []
for k in range(l):
data.append([k])
print(data)
In the above code, the expression [k] creates a new list that contains only the int value k.
Simpler yet:
l = 4
data = [[k] for k in range(l)]
print(data)
l = 4
data = []
for k in range(l):
data.append([k])
print(data)
None will also stay in the list. You are not removing it. Also, it creates a reference to the same list. so, appending to it will modify all the inner lists.
Instead of appending to an inner list, you can append a list to the list with only one element in it.
Or you can use a list comprehension:
print([[k] for k in range(4)])

Python: why appending to a list results in same values

This is my code:
a = []
res = []
for i in range(0, 3):
a.append(i)
res.append(a)
print(res)
The result is:
[[0, 1, 2], [0, 1, 2], [0, 1, 2]]
But I want the result to be:
[[0], [0, 1], [0, 1, 2]]
I know the solution is using the shallow copy: res.append(a[:]). But can somebody tell me why?
You appended the same thing (a) to res three times, so it appears 3 times. The fact that you changed the contents of a between each call to append doesn't matter. If each call to append was given its own copy of a, then you'd get the result you expect.
when you append the object "a", python actually appends a pointer that points to the original list "a". In the next iteration of the loop you change the original object, so all of the pointers to that object show the latest state of the object.
you can add print the lists in every iteration to see that in action.
What you want is to create a copy of "a", that would remain unchanged, and append the copy to "res". Like you said, using the syntax a[:] would do the job.
When you append a to the res array, you are appending a pointer to the a variable. Not the 'value' of the a variable. So when you are done, the res array has the 'value' - [a, a, a].
When you shallow copy, you are copying the 'value' of the a variable at that stage of the loop into the res array, giving you a 'value' of - [[0],[0,1],[0,1,2]].
a = [] # (1) list created here
res = []
for i in range(0, 3):
a.append(i) # (2) list modified here
res.append(a) # (3) res is modified here
print(res)
What your code is saying is this:
At (1) a list is created and a refers to this list.
At (2) you modify the list from (1), but the list itself remains at the same memory location and a still refers to this list.
At (3) you just make a copy of a reference and add it to res, and still neither a nor the list at (1) change.
The end results is that res gets 3 copies of the reference to the list at (1).
Here is a side effect:
a[1] = 42
print(res)
Output:
[[0, 42, 2], [0, 42, 2], [0, 42, 2]]
You say that you know that this is the code you are after:
a = []
res = []
for i in range(0, 3):
a.append(i)
res.append(a[:]) # (4) new list created
print(res)
At (4) a new list is created whose contents is the same as the list that a refers to. This new list is not referred to by a, but instead, one of the elements of res has a reference to this new list.
Firstly this means that res holds references to lists, which is why they hang around long enough to be printed. Secondly a still refers to the original list.
Here is a side effect:
a[1] = 42
print(res)
Output:
[0, 42, 2] [[0], [0, 1], [0, 1, 2]]
However, this is not the end of the story if you examine this code:
a = []
res = []
for i in range(0, 3):
a.append([i]) # (5) Create a new list with one element
res.append(a[:]) # (6) Shallow copy as above
print(res)
a[1].append(42)
print(a, res)
Output:
[[[0]], [[0], [1]], [[0], [1], [2]]]
[[0], [1, 42], [2]] [[[0]], [[0], [1, 42]], [[0], [1, 42], [2]]]
This occurs because at (6) there was only a shallow copy made.

Categories