18

I'm using python 3.4.1.
For a single list a=[1,2], if I make a copy of it, b = a.copy() when I change items in b, it won't change items in a.
However, when I define a list of lists (actually a matrix) a = [[1,2],[3,4]], when I assign b = a.copy(). What I do to list b actually affects a.
I checked their addresses, they are different.
Can anyone tell me why?

ps: What I did is b[0][0] = x, and the item in a was also changed.

Thomas8
  • 1,117
  • 1
  • 11
  • 22
jack
  • 883
  • 2
  • 9
  • 21

3 Answers3

26

From the docs for the copy module:

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.

When you call regular copy.copy() you are performing a shallow copy. This means that in a case of a list-of-lists, you will get a new copy of the outer list, but it will contain the original inner lists as its elements. Instead you should use copy.deepcopy(), which will create a new copy of both the outer and inner lists.

The reason that you didn't notice this with your first example of using copy([1,2]) is that the primitives like int are immutable, and thus it is impossible to change their value without creating a new instance. If the contents of the list had instead been mutable objects (like lists, or any user-defined object with mutable members), any mutation of those objects would have been seen in both copies of the list.

aruisdante
  • 8,875
  • 2
  • 30
  • 37
  • Thank you! And one more question about copy. Can I get a copy of a class? Like the tree class, can I get a copy of some tree object t1 using a simple way? – jack Feb 23 '15 at 22:04
  • Yes, you can use the `copy` module on any object that defines the `__copy__` and `__deepcopy__` methods. See the linked documentation in the original answer for specifics. There is not, sadly, a 'pre-defined' copy operation in Python like there is in languages like C++ (which is usually a good thing, because C++'s auto-generated copy-constructor is often wrong for any non-trivial class). – aruisdante Feb 23 '15 at 22:07
  • @jack You can check what is going on when you use the copy method on [pythontutor.com](http://www.pythontutor.com/visualize.html#code=a+%3D+%5B%5B1,+2%5D,+%5B3,+4%5D%5D%0Ab+%3D+a.copy()%0Ab%5B0%5D%5B0%5D+%3D+9&mode=display&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=3&rawInputLstJSON=%5B%5D&curInstr=0). You can see all of the pointers visualized there. – Arpad Horvath -- Слава Україні Feb 23 '15 at 22:13
10

Perhaps a list comprehension as such:

new_list = [x[:] for x in old_list]

...though if your matrices are deeper than one layer, list comprehension is probably less elegant than just using deepcopy.

edit - a shallow copy, as stated, will still contain references to the objects inside the list. So for example...

>>> this = [1, 2]
>>> that = [33, 44]
>>> stuff = [this, that]
>>> other = stuff[:]
>>> other
[[1, 2], [33, 44]]
>>> other[0][0] = False
>>> stuff
[[False, 2], [33, 44]]    #the same problem as before
>>> this
[False, 2]                #original list also changed
>>> other = [x[:] for x in stuff]
>>> other
[[False, 2], [33, 44]]
>>> other[0][0] = True
>>> other
[[True, 2], [33, 44]]
>>> stuff
[[False, 2], [33, 44]]    #copied matrix is different
>>> this
[False, 2]                #original was unchanged by this assignment
  • 1
    It's also a lot less clear to a casual reader what it's doing than using methods from `copy`, though it is slightly more performant. – aruisdante Feb 23 '15 at 22:02
  • Sure, there's that. But list comprehensions are most pythonic, so I guess it depends on what is considered 'clear', and to whom. In any event, if the matrix is too nested then my suggestion wouldn't be very helpful, but for a single dimension I don't think it is too bad –  Feb 23 '15 at 22:05
-21

It's very simply, just do that:

b = a

Exemple:

>>> a = [1, 2, 3]
>>> b = a
>>> b.append(4)
>>> b
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]
Texom512
  • 4,785
  • 3
  • 16
  • 16