List of lists changes reflected across sublists unexpectedly
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.
Changing an element in one list changes multiple lists
What matters is how you created your original mysolution
list. As it seems, it contains four times the same list which is why changing it once will make it change in all four locations.
To initialize independent zero-filled lists like that, you can do the following:
mysolution = [[0] * 4 for i in range(4)]
Assignment of variable to list using indexing
Your code reads:
def func(first):
third = first[0]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
What's happening is this:
- In
func()
, the argumentfirst
contains a reference to a list of lists with value[[3,4]]
. - After the assignment to
third
,third
contains a reference to the list[3,4]
at position 0 in the list referenced byfirst
. No new list object has been created and no copy of a list has taken place, rather a new reference to the existing list has been created and stored in the variablethird
. - In the line
first[0][0] = 5
, the item at position 0 in the list[3,4]
is updated so that the list is now[5,4]
. Note that the list[3,4]
that was modified is an element of the list of lists referenced byfirst
, and it is also the one referenced bythird
. Because the object (namely, the list) that is referenced bythird
has now been modified, any use of this reference to access the list (such asprint(third)
) will reflect its updated value, which is[5,4]
.
UPDATE:
The code for your updated question is:
def func(first):
third = first[0][0:2]
first[0][0] = 5
print(third)
first = [[3,4]]
func(first)
In this case, the assignment third = first[0][0:2]
takes a slice of the list [3,4]
at position 0 in the list of lists referenced by first
. Taking a slice in this way creates a new object which is a copy of the subsequence indicated by the arguments specified in the square brackets, so after the assignment, the variable third
contains a reference to a newly created list with value [3,4]
. The subsequent assignment first[0][0] = 5
updates the value of the list [3,4]
in position 0 of the list of lists referenced by first
, with the result that the value of the list becomes [5,4]
, and has no effect on the value of third
which is an independent object with value [3,4]
.
Importantly (and potentially confusingly), slice notation used on the left-hand side of an assignment works very differently. For example, first[0][0:2] = [5,4]
would change the contents of the list first[0]
such that the elements in index 0 and 1 are replaced by [5,4]
(which in this case means the value of the list object would be changed from [3,4]
to [5,4]
, but it would be the same object).
Related Topics
How to Update a Python Package
Splitting Out the Output of Ps Using Python
Tkinter: Attributeerror: Nonetype Object Has No Attribute ≪Attribute Name≫
Selenium "Selenium.Common.Exceptions.Nosuchelementexception" When Using Chrome
How to Keep Keys/Values in Same Order as Declared
Pip' Is Not Recognized as an Internal or External Command
How to Do Relative Imports in Python
How to Do a Case-Insensitive String Comparison
How to Protect Python Code from Being Read by Users
Why Is Nothing Drawn in Pygame At All
Post Values from an HTML Form and Access Them in a Flask View
Pass Input/Variables to Command/Script Over Ssh Using Python Paramiko
How to Make Python Script Run as Service
Force Another Program'S Standard Output to Be Unbuffered Using Python
How to Sort a Dictionary by Value