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.
Related
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
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)])
This question already has answers here:
What is the difference between shallow copy, deepcopy and normal assignment operation?
(12 answers)
Closed 7 years ago.
Here is a problem I had to do:
We are going to implement a very helpful function, called group.
group takes a list of things and returns a list of group, where each group is formed by all equal consecutive elements in the list.
For example:
group([1, 1, 1, 2, 3, 1, 1]) == [[1, 1, 1], [2], [3], [1, 1]]
group([1, 2, 1, 2, 3, 3]) == [[1], [2], [1], [2], [3, 3]]
And here is my initial solution:
def group(int_list):
group_list = []
current_list = []
for i in range(len(int_list)):
if int_list[i] not in current_list:
if len(current_list) != 0:
group_list.append(current_list)
del current_list[:]
current_list.append(int_list[i])
else:
current_list.append(int_list[i])
group_list.append(current_list)
return group_list
And the output I was getting:
[[1, 1], [1, 1], [1, 1], [1, 1]]
After spending like 30 minutes trying to figure out the problem, I changed the 9th line from group_list.append(current_list) to group_list.append(current_list[:]) and surprisingly the magic worked. I got the correct output:
[[1, 1, 1], [2], [3], [1, 1]]
So I guess my question is what's the difference between current_list and current_list[:] ?
current_list[:] is a shallow copy of current_list; e.g.,:
Introducing lists: Slicing shorthand
Python list slice syntax used for no obvious reason
"copying a list the right way"
In your function, you're building up a list (of the current group) in the thing referred to by current_list. When you're done, you add this thing to group_list, and then reset the thing by deleting all of its contents (del current_list[:]). We have to remember that everything in Python is a reference, so, using your first code, group_list contains several references to the same object (this is why your output looks like [[1, 1], [1, 1], [1, 1], [1, 1]]). When you delete the contents of current_list and add new elements later on, you do this to every element of group_list, too.
Using the current_list[:] syntax that you discovered, you create a copy of current_list and add this onto group_list; this copy is not modified later when you delete the contents of current_list.
Basicaly the main difference is that current_list is the reference to the list, and current_list[:] is a new array with the elements of the list. So, using the first one, when you change current_list, group_list is also changed. The other way if you change current_list, group_list is not modified.
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]
I've just started programming, and am working my way through "How to think like a Computer Scientist" for Python. I haven't had any problems until I came to an exercise in Chapter 9:
def add_column(matrix):
"""
>>> m = [[0, 0], [0, 0]]
>>> add_column(m)
[[0, 0, 0], [0, 0, 0]]
>>> n = [[3, 2], [5, 1], [4, 7]]
>>> add_column(n)
[[3, 2, 0], [5, 1, 0], [4, 7, 0]]
>>> n
[[3, 2], [5, 1], [4, 7]]
"""
The code should make the above doctest pass. I was getting stuck on the last test: getting the original list to stay unaffected. I looked up the solution, which is the following:
x = len(matrix)
matrix2 = [d[:] for d in matrix]
for z in range(x):
matrix2[z] += [0]
return matrix2
My question is this: why can't the second line be:
matrix2 = matrix[:]
When this line is in place the original list gets edited to include the addition elements. The "How to be.." guide makes it sound like cloning creates a new list that can be edited without affecting the original list. If that were true, what's going on here? If I use:
matrix2 = copy.deepcopy(matrix)
Everything works fine, but I wasn't under the impression that cloning would fail...
any help would be greatly appreciated!
In your case, matrix contains other lists, so when you do matrix[:], you are cloning matrix, which contains references to other lists. Those are not cloned too. So, when you edit these, they are still the same in the original matrix list. However, if you append an item to the copy (matrix[:]), it will not be appended to the original list.
To visualize this, you can use the id function which returns a unique number for each object: see the docs.
a = [[1,2], [3,4], 5]
print 'id(a)', id(a)
print '>>', [id(i) for i in a]
not_deep = a[:]
# Notice that the ids of a and not_deep are different, so it's not the same list
print 'id(not_deep)', id(not_deep)
# but the lists inside of it have the same id, because they were not cloned!
print '>>', [id(i) for i in not_deep]
# Just to prove that a and not_deep are two different lists
not_deep.append([6, 7])
print 'a items:', len(a), 'not_deep items:', len(not_deep)
import copy
deep = copy.deepcopy(a)
# Again, a different list
print 'id(deep)', id(deep)
# And this time also all the nested list (and all mutable objects too, not shown here)
# Notice the different ids
print '>>', [id(i) for i in deep]
And the output:
id(a) 36169160
>> [36168904L, 35564872L, 31578344L]
id(not_deep) 35651784
>> [36168904L, 35564872L, 31578344L]
a items: 3 not_deep items: 4
id(deep) 36169864
>> [36168776L, 36209544L, 31578344L]
Say you have nested lists, copying will only copy the references to those nested lists.
>>> a = [1]
>>> b = [2]
>>> c = [a, b]
>>> c
[[1], [2]]
>>> d = c[:]
>>> d
[[1], [2]]
>>> d[1].append(2)
>>> d
[[1], [2, 2]]
>>> c
[[1], [2, 2]]
As where, with copy.deepcopy():
>>> d = copy.deepcopy(c)
>>> d[1].append(2)
>>> c
[[1], [2]]
>>> d
[[1], [2, 2]]
This is true of any mutable items. copy.deepcopy() will attempt to make sure that they are copied too.
It's also worth noting that using d = c[:] to copy a list isn't a very clear syntax anyway. A much better solution is d = list(c) (list() returns a new list from any iterable, including another list). Even more clear, obviously, is copy.copy().