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)
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 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.
Changes made in Python list showing up in copy of original list
Use copy.deepcopy
:
from copy import deepcopy
desc1 = deepcopy(desc)
list.copy
only makes a shallow copy: a new list
object, but its elements (here mutable sublists) will be references to the same objects as the elements of the original.
How do I clone a list so that it doesn't change unexpectedly after assignment?
new_list = my_list
doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so both new_list
and my_list
refer to the same list after the assignment.
To actually copy the list, you have several options:
You can use the builtin
list.copy()
method (available since Python 3.3):new_list = old_list.copy()
You can slice it:
new_list = old_list[:]
Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).
You can use the built in
list()
constructor:new_list = list(old_list)
You can use generic
copy.copy()
:import copy
new_list = copy.copy(old_list)This is a little slower than
list()
because it has to find out the datatype ofold_list
first.If you need to copy the elements of the list as well, use generic
copy.deepcopy()
:import copy
new_list = copy.deepcopy(old_list)Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).
Example:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return f'Foo({self.val!r})'
foo = Foo(1)
a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
foo.val = 5
print(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')
Result:
original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
Changing list in dictionary changes all lists
from copy import deepcopy
empty = []
for x in range(2):
empty.append([])
for y in range(2):
empty[x].append(False)
status = {k:[] for k in ["a", "b", "c"]}
status["a"] = deepcopy(empty)
status["b"] = deepcopy(empty)
status["c"] = deepcopy(empty)
print(status)
status["a"][0][0] = True
print(status)
you were close with the copy, but you actually neeed a deepcopy
Related Topics
What Is Memoization and How to Use It in Python
How to Merge Lists into a List of Tuples
Can a Variable Number of Arguments Be Passed to a Function
Prevent Scientific Notation in Matplotlib.Pyplot
Why Do Python Classes Inherit Object
What Is a Mixin and Why Is It Useful
How to Print a Number Using Commas as Thousands Separators
How to Sort a List of Objects Based on an Attribute of the Objects
All Combinations of a List of Lists
C Function Called from Python Via Ctypes Returns Incorrect Value
Why Is _Init_() Always Called After _New_()
How to Get Line Count of a Large File Cheaply in Python
How to Step Through Python Code to Help Debug Issues
How to Explicitly Free Memory in Python