8
votes

I thought i knew Python until tonight. What is the correct way to do something like this? Here's my code:

a = ["one", "two", "three"]
b = a  # here I want a complete copy that when b is changed, has absolutely no effect on a
b.append["four"]
print a  # a now has "four" in it

Basically i want to know, instead of the b = a step, how would I correctly make a copy of a list or dictionary so that when b is changed a does not change along with it?

3
Well, you don't know Python. Or at least you don't know an important part of it, what variables are. See similar question on SO or e.g. python.net/~goodger/projects/pycon/2007/idiomatic/…. - user395760
@delnan to be fair, it's not an uncommon pitfall to folks who are new to python to understand that variable assignment involving lists assigns references, though the claim "I thought I knew Python" tends to indicate they're not "new" :) - Daniel DiPaolo

3 Answers

14
votes

What you are experiencing is the concept of references. All objects in Python have a reference and when you assign one to two names a and b, this results in both a and b pointing to the same object.

>>> a = range(3)
>>> b = a                     # same object
>>> b.append(3)
>>> a, b                      # same contents
([0, 1, 2, 3], [0, 1, 2, 3])

With lists, you can make create a new list b that is a copy of another a using b = a[:].

>>> a = range(3)
>>> b = a[:]                  # make b a new copy of a
>>> b.append(3)
>>> a, b                      # a is left unchanged
([0, 1, 2], [0, 1, 2, 3])

For a more general solution for any object, use the copy module. A shallow copy will copy the references stored within the object you're copying, whereas a deep copy will recursively make new copies of all objects.

>>> a = [range(2), range(3)]
>>> b = copy.copy(a)          # shallow copy of a, equivalent to a[:]
>>> b[0] = range(4)
>>> a, b                      # setting an element of b leaves a unchanged
([[0, 1], [0, 1, 2]], [[0, 1, 2, 3], [0, 1, 2]])
>>> b[1].append(3)
>>> a, b                      # modifying an element of b modifies the element in a
([[0, 1], [0, 1, 2, 3]], [[0, 1, 2, 3], [0, 1, 2, 3]])

>>> a = [range(2), range(3)]
>>> b = copy.deepcopy(a)      # deep recursive copy of a
>>> b[1].append(3)
>>> a, b                      # modifying anything in b leaves a unchanged
([[0, 1], [0, 1, 2]], [[0, 1], [0, 1, 2, 3]])
1
votes

The correct way of copying an object is

 from copy import copy
 a = [1, 2, 3]
 b = copy(a)

Pretty simple. There is shortcuts for lists:

 a = [1, 2, 3]
 b = a[:]

There is also deepcopy() if you want to copy the objects in the list as well.

1
votes

Here are 3 ways to make a copy of list a:

Use slice notation:

copy_of_a = a[:]

Use the list constructor:

copy_of_a = list(a)

Use the copy module:

from copy import copy
copy_of_a = copy(a)

These are all shallow copies, which is sufficient for your question. To learn about the difference between shallow copy and deep copy read the documentation of the copy module.