Why does .append() affect all elements in a list of lists?
Asked Answered
E

4

22

I create a list of lists and want to append items to the individual lists, but when I try to append to one of the lists (a[0].append(2)), the item gets added to all lists.

a = []
b = [1]

a.append(b)
a.append(b)

a[0].append(2)
a[1].append(3)
print(a)

Gives: [[1, 2, 3], [1, 2, 3]]

Whereas I would expect: [[1, 2], [1, 3]]

Changing the way I construct the initial list of lists, making b an int instead of a list and putting the brackets inside .append(), gives me the desired output:

a = []
b = 1

a.append([b])
a.append([b])

a[0].append(2)
a[1].append(3)
print(a)

Gives: [[1, 2], [1, 3]]

But why? It is not intuitive that the result should be different. I know this has to do with there being multiple references to the same list, but I don't see where that is happening.

Enter answered 15/6, 2011 at 15:31 Comment(1)
Related: How do I clone a list so that it doesn't change unexpectedly after assignment? (There, the new reference is a name, but here it's an element of a list.) Also: List of lists changes reflected across sublists unexpectedlyRusell
F
39

It is because the list contains references to objects. Your list doesn't contain [[1 2 3] [1 2 3]], it is [<reference to b> <reference to b>].

When you change the object (by appending something to b), you are changing the object itself, not the list that contains the object.

To get the effect you desire, your list a must contain copies of b rather than references to b. To copy a list you can use the range [:]. For example:

>>> a = []
>>> b = [1]
>>> a.append(b[:])
>>> a.append(b[:])
>>> a[0].append(2)
>>> a[1].append(3)
>>> print a
[[1, 2], [1, 3]]
Fetching answered 15/6, 2011 at 15:33 Comment(0)
D
2

The key is this part:

a.append(b)
a.append(b)

You are appending the same list twice, so both a[0] and a[1] are references to the same list.

In your second example, you are creating new lists each time you call append like a.append([b]), so they are separate objects that are initialized with the same float value.

Dual answered 15/6, 2011 at 15:34 Comment(1)
Thanks, this answer also added to my understanding.Enter
T
1

In order to make a shallow copy of a list, the idiom is

a.append(b[:])

which when doubled will cause a to have two novel copies of the list b which will not give you the aliasing bug you report.

Tarweed answered 15/6, 2011 at 15:37 Comment(0)
S
0

The problem is that 'a' has the same list twice. Using:

https://pypi.org/project/memory-graph/

you can graph your data and better understand what data is shared. When I alter your code to graph your data:

import memory_graph # see install instructions at link above
a = []
b = [1]

a.append(b)
a.append(b)

a[0].append(2)
a[1].append(3)
print(a)
memory_graph.d() # draw graph

I get:

enter image description here

and it is easy to see that 'a' has the same list twice which you can not see from just printing 'a'. The solution could be to copy 'b' like:

import memory_graph
a = []
b = [1]

a.append(b.copy()) # <------ copy
a.append(b.copy()) # <------ copy

a[0].append(2)
a[1].append(3)
print(a)
memory_graph.d()

Which results in:

enter image description here

and gives the expected output: [[1, 2], [1, 3]]

Full disclosure: I am the developer of memory_graph.

Sear answered 20/2, 2024 at 20:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.