Categories

# List of lists changes reflected across sublists unexpectedly

I created a list of lists:

``````xs = [ * 4] * 3

# xs == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
``````

Then, I changed one of the innermost values:

``````xs = 5

# xs == [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
``````

Why did every first element of each sublist change to `5`?

• Note that the same logic applies to a list of dicts, because of the same fundamental problem of aliasing a mutable object. See stackoverflow.com/questions/46835197/… for a more specific question.

Dec 16, 2021 at 23:17

• Are there more specific questions for when the list of lists is created in other ways (but has the same problem)? For example, by using `.append` in a loop?

May 3 at 14:52

• See also stackoverflow.com/questions/2612802 for a question focused on avoiding this kind of aliasing after the fact.

Jun 4 at 12:40

• Jun 22 at 0:59

When you write `[x]*3` you get, essentially, the list `[x, x, x]`. That is, a list with 3 references to the same `x`. When you then modify this single `x` it is visible via all three references to it:

``````x =  * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(xs): {id(xs)}\n"
f"id(xs): {id(xs)}\n"
f"id(xs): {id(xs)}"
)
# id(xs): 140560897920048
# id(xs): 140560897920048
# id(xs): 140560897920048

x = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
``````

To fix it, you need to make sure that you create a new list at each position. One way to do it is

``````[*4 for _ in range(3)]
``````

which will reevaluate `*4` each time instead of evaluating it once and making 3 references to 1 list.

You might wonder why `*` can’t make independent objects the way the list comprehension does. That’s because the multiplication operator `*` operates on objects, without seeing expressions. When you use `*` to multiply `[ * 4]` by 3, `*` only sees the 1-element list `[ * 4]` evaluates to, not the `[ * 4` expression text. `*` has no idea how to make copies of that element, no idea how to reevaluate `[ * 4]`, and no idea you even want copies, and in general, there might not even be a way to copy the element.

The only option `*` has is to make new references to the existing sublist instead of trying to make new sublists. Anything else would be inconsistent or require major redesigning of fundamental language design decisions.

In contrast, a list comprehension reevaluates the element expression on every iteration. `[ * 4 for n in range(3)]` reevaluates ` * 4` every time for the same reason `[x**2 for x in range(3)]` reevaluates `x**2` every time. Every evaluation of ` * 4` generates a new list, so the list comprehension does what you wanted.

Incidentally, ` * 4` also doesn’t copy the elements of ``, but that doesn’t matter, since integers are immutable. You can’t do something like `1.value = 2` and turn a 1 into a 2.

• I am surprised that no body points out that, the answer here is misleading. `[x]*3` store 3 references like `[x, x, x]` is only right when `x` is mutable. This does’t work for e.g. `a=*3`, where after `a=5`, `a=[5,4,4].`

May 22, 2015 at 0:16

• Technically, it’s still correct. `*3` is essentially equivalent to `x = 4; [x, x, x]`. It’s true, though, that this will never cause any problem since `4` is immutable. Also, your other example isn’t really a different case. `a = [x]*3; a = 5` won’t cause problems even if `x` is mutable, since you’re not modifying `x`, only modifying `a`. I wouldn’t describe my answer as misleading or incorrect – you just can’t shoot yourself in the foot if you’re dealing with immutable objects.

May 22, 2015 at 8:04

• @Allanqunzi you are wrong. Do `x = 1000; lst = [x]*2; lst is lst` -> `True`. Python does not distinguish between mutable and immutable objects here whatsoever.

Apr 17, 2016 at 18:08

• can anyone find documents about the `*` operator in docs.python.org? i tried but cnanot find any.

Mar 31 at 1:46

• @LeiYang It’s listed under Common Sequence Operations

May 22 at 4:56

``````size = 3
matrix_surprise = [ * size] * size
matrix = [*size for _ in range(size)]
``````

Live visualization using Python Tutor: • So, why if we write matrix= [[x] * 2] doesn’t make 2 elemnts for the same object like the example you describe, it seems to be the same concept, what am i missing?

Jul 1, 2017 at 17:55

• @AhmedMohamed Indeed it does make a list with two elements of the exact same object that `x` refers to. If you make a globally unique object with `x = object()` and then make `matrix = [[x] * 2]` these does come as true: `matrix is matrix`

Jul 2, 2017 at 13:13

• @nadrimajstor so why the change in matrix doesn’t affect matrix like the example above with 2d matrix.

Jul 2, 2017 at 13:31

• @AhmedMohamed Surprise come when you make a “copy” of mutable sequence (in our example it is a `list`) so if a `row = [x] * 2` than a `matrix = [row] * 2` where both rows are exactly the same object, and now changes to one row `matrix = y` suddenly reflect in the other one `(matrix is matrix) == True`

Jul 2, 2017 at 15:44

• @AhmedMohamed Take a look at Ned Batchelder – Facts and Myths about Python names and values as it might offer a better explanation. 🙂

Jul 2, 2017 at 16:11

Actually, this is exactly what you would expect. Let’s decompose what is happening here:

You write

``````lst = [ * 4] * 3
``````

This is equivalent to:

``````lst1 = *4
lst = [lst1]*3
``````

This means `lst` is a list with 3 elements all pointing to `lst1`. This means the two following lines are equivalent:

``````lst = 5
lst1 = 5
``````

As `lst` is nothing but `lst1`.

To obtain the desired behavior, you can use a list comprehension:

``````lst = [ *4 for n in range(3) ]
``````

In this case, the expression is re-evaluated for each `n`, leading to a different list.

• Just a small addition to the nice answer here: it’s evident that you’re dealing with same object if you do `id(lst)` and `id(lst)` or even `id(lst)` and `id(lst)`

May 17, 2017 at 7:08

• Doesn’t explain why modifying a 1d list causes a copy while a 2d list doesn’t cause any copy

Jun 10 at 17:55