I created a list of lists:
xs = [[1] * 4] * 3
# xs == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Then, I changed one of the innermost values:
xs[0][0] = 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
?
Python list can contain duplicate elements.
The SQL DISTINCT keyword, which we have already discussed is used in conjunction with the SELECT statement to eliminate all the duplicate records and by fetching only the unique records.
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 = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(xs[0]): {id(xs[0])}\n"
f"id(xs[1]): {id(xs[1])}\n"
f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048
x[0] = 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
[[1]*4 for _ in range(3)]
which will reevaluate [1]*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 [[1] * 4]
by 3, *
only sees the 1-element list [[1] * 4]
evaluates to, not the [[1] * 4
expression text. *
has no idea how to make copies of that element, no idea how to reevaluate [[1] * 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. [[1] * 4 for n in range(3)]
reevaluates [1] * 4
every time for the same reason [x**2 for x in range(3)]
reevaluates x**2
every time. Every evaluation of [1] * 4
generates a new list, so the list comprehension does what you wanted.
Incidentally, [1] * 4
also doesn't copy the elements of [1]
, 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.
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]
Live visualization using Python Tutor:
Actually, this is exactly what you would expect. Let's decompose what is happening here:
You write
lst = [[1] * 4] * 3
This is equivalent to:
lst1 = [1]*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[0][0] = 5
lst1[0] = 5
As lst[0]
is nothing but lst1
.
To obtain the desired behavior, you can use a list comprehension:
lst = [ [1]*4 for n in range(3) ]
In this case, the expression is re-evaluated for each n
, leading to a different list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With