Changing an Element in One List Changes Multiple Lists

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

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 one list unexpectedly changes another, too

Why does v change at all?

vec and v are both references.

When coding vec = v you assign v address to vec.
Therefore changing data in v will also "change" vec.

If you want to have two different arrays use:

vec = list(v)

Why does my code change multiple list elements when I'm only trying to change one?

The problem comes from the array pointer (see C language). Only one 2D table in memory, the other tables point to this single table.

Solution:

mesh = [ [ [1 for x in range(2)] for i in range(2) ] for j in range(2) ]
mesh[0][0][1] = 555
print(mesh)

Multiple values change when changing a single value in a nested list

When you create grid = [[0]*10]*10, you are actually creating an array of references to the same object (a list of [0]s). So when you change the object via one reference, this changes all entries in the list.

This is a very common Python 'gotcha' for beginners.

Replacing an element in a list with multiple elements

You can use slice assignment:

>> l1 = [[1, 2], [3, 4]]
>>> l2 = [[1], [2]]
>>> l1[0:1] = l2
>>> l1
[[1], [2], [3, 4]]

This changes l1, so if you want to keep it make a copy before.

Another way that doesn't change l1 is addition:

>> l1 = [[1, 2], [3, 4]]
>>> l3 = l2 + l1[1:]
>>> l3
[[1], [2], [3, 4]]

Iterate through multiple lists to apply the same changes to each

You're on the right track here:

for each in lists:
each = [(x + 'z') for x in each]

The trouble here is that each is the list you're working on initially, but afterward, each is the new list. You're changing the variable each, not the original list.

This is easily remedied by using a slice assignment:

for each in lists:
each[:] = [(x + 'z') for x in each]

In other words, you're replacing the contests of each (which is still the original list) with the new list, and this gets you the result you're looking for.

Changing one list changed the other list

What you did was a shallow copy. The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):

A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

What you want to do it to create a whole new object so that one will not interfere with other. Here is how to do a deep copy.

import copy

a = [[1, 1], [2, 2], [3, 3]]
b = copy.deepcopy(a)
a[0][0] = 100
print(a)
print(b)

Changing one list changed the other list

What you did was a shallow copy. The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):

A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

What you want to do it to create a whole new object so that one will not interfere with other. Here is how to do a deep copy.

import copy

a = [[1, 1], [2, 2], [3, 3]]
b = copy.deepcopy(a)
a[0][0] = 100
print(a)
print(b)


Related Topics



Leave a reply



Submit