Morty
Morty

Reputation: 95

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?

Upvotes: 1

Views: 3770

Answers (4)

quamrana
quamrana

Reputation: 39354

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.

Upvotes: 1

geocave
geocave

Reputation: 21

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]].

Upvotes: 2

Idan Dardikman
Idan Dardikman

Reputation: 21

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.

Upvotes: 2

Scott Hunter
Scott Hunter

Reputation: 49803

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.

Upvotes: 6

Related Questions