Skytunnel
Skytunnel

Reputation: 1083

Python: copying a list within a list

Hoping someone can help me here.

I'm very new to Python, and I'm trying to work out what I'm doing wrong.

I've already searched and found out that that Python variables can be linked so that changing one changes the other, and I have done numerous tests with the id() function to get to grips with this concept. But I seem to have found an exception I'm hoping someone can explain...

firstly, the following works as expected to make a independent copy of a list.

>>> a = [0,0]
>>> b = a[:]
>>> print a is b
False
>>> b[0]=1
>>> print a
[0,0]
>>> print b
[1,0]

But if I change this slightly so that a is list within a list it changes...

>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> print a is b
False
>>> b[0][0]=1
>>> print a
[[1, 0], [0, 0]]
>>> print b
[[1, 0], [0, 0]]

Now we see that any update of b will also apply to a, but yet the result of print a is b returns False?? I've check this against id() as well, everything says they are independent of each other, but when I update one the same applies to the other??

Can anyone explain this??

Noting I'm running these of http://labs.codecademy.com/#:workspace, so my first thought is that it's just a bug on their site, but I don't know?

EDIT:

THANK YOU ALL for the great answers so far. That was fast! I know this has probably been asked before but it was difficult thing to search for.

Since all the answers are correct, I'll wait a day before marking. whoever has the most +1's will get the mark :)

Upvotes: 19

Views: 581

Answers (7)

Barleyman
Barleyman

Reputation: 165

There's an alternative to the computationally expensive deepcopy when you're dealing with lists inside lists

origvector=[]
    for ind in range(0, len(testvector)):
        origvector.append(testvector[ind][:])

In this example the "testvector" is a matrix of n vectors, each item containing a three-item list. Like this:

{0,1,2}{10,20,30}
{3,4,5}{40,50,60}
{6,7,8}{70,80,90}

Upvotes: 0

Bakuriu
Bakuriu

Reputation: 101919

I believe the simplest way to get what's happening is to use a visual representation(the idea of this representation is not mine, although I love it).

First of all you have to understand that in python there are only references to objects. The objects themselves live separately one from the other. For example the list [0, 1] is a list-object that contains references to the object 0 and the object 1. A reference is some kind of link. This is different from variables in other languages, since variables are generally memory locations where you put things. In python a "variable", i.e. an identifier, is simply a "name"(=reference) for an object.

To understand this, let's picture the relationships between objects with a metaphor: Let's say that objects are heavy rocks in the sea, that are linked together by ropes and hooks (¿). On the surface of the sea live the identifiers that refers to the objects. The identifiers are buoys that prevent objects from sinking in the depths(where, they say, sea monsters(aka the Garbage Collector) would destroy them).

For example, we can represent this situation:

a = [0, 1]

With the following diagram:

         ___
        (   )
~~~~~~~~( a )~~~~~~~~
        (___)
 o        ¿      o
          |       O
          |    o
          |
          |
   +------+-------+
   | [  ¿   , ¿ ] |
   +----|-----|---+
        |     |
        |     |
   o    |     |
 O      |     |
        |     |
      +-+-+ +-+-+
      | 0 | | 1 |
      +---+ +---+

 o                 O  o
    )
   (  )             o
  ) )(  )        ( (
 ( (  )( (      ( ) )

As you can see the identifier a refers, i.e. is linked with a rope, to the list object. The list-object has two slots, each containing a link connected to the objects 0 and 1.

Now, if we did:

b = a

The identifier b would refer to the same object of a:

            ___                 ___
           (   )               (   )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
           (___)               (___)
             ¿                   ¿
              \                 /
    o          \               /             o
  o             \             /            o
                -------+-------
     O         |  [ ¿  ,  ¿ ]  |              O
                ----|-----|----
                    |     |
                  +-+-+ +-+-+
         o        | 0 | | 1 |
                  +---+ +---+              o
    O
       o                              O
                                         o


               )
            ) (                    ) (
         ( (   )(               ( (   )
        ( ) ) (  ) (           ( ) ) (  )

When you, instead, do a shallow copy of a, via:

b = a[:]

A new list is created, and its elements are copies of the references to the objects referred to by a, i.e. you made copies of the ropes, but they point to the same elements:

               ___                 ___
               (   )               (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)               (___)
    O            ¿                   ¿               o
                 |                   |
       o         |                   |
                 |                   |
          -------+------       ------+-------
         |  [ ¿  , ¿ ]  |     |  [ ¿ ,  ¿  ] |
          ----|----|----       ----|----|----
              |    |               |    |
               \    \             /    /
                \    \           /    /
                 \    \         /    /            o
    o             \    \       /    /           o
                   \    \     /    /               o
       o            \    \   /    /
                     \    \ /    /             o
   O                  \    X    /
                       \  / \  /
                        \/   \/
                        |     |
                        |     |
                        |     |
                      +-+-+ +-+-+
                      | 0 | | 1 |
                      +---+ +---+



                 )
           (    (                  )      (
     )(     )    )  )           ( (   )    )  )
    (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

Since integers are immutable, there isn't any difference between using copies or the same identical objects, but when you replace the integers with lists, which are mutable, you end up modifying references to the same object, hence the behaviour you see.

Visually, the code:

a = [[0, 1], [0, 1]]
b = a[:]

Results in:

                ___                 ___
               (   )               (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)               (___)
    O            ¿                   ¿               o
                 |                   |
       o         |                   |
                 |                   |
          -------+------       ------+-------
         |  [ ¿  , ¿ ]  |     |  [ ¿ ,  ¿  ] |
          ----|----|----       ----|----|----
              |     \             /     |
              |      \           /      |
              |       \         /       |
              |        \       /        |
              |         \     /         |
              |          \   /          |
              |           \ /           |
              |            X            |
              |           / \           |
              |          /   \          |
              |         /     \         |
              |        /       \        |
              |       /         \       |
              |      /           \      |
              |     |             \     |
              |     |              |    |
         +----+-----+----+   +-----+----+----+
         | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |
         +----|-----|----+   +----|-----|----+
               \     \           /     /
                \     \         /     /
                 \     \       /     /
                  \     \     /     /
                   \     \   /     /
                    \     | /     /
                     |    |/     /
                     |    X     /
                     |   / |   /
                     |  /  |  /
                     \ /   \ /
                      Y     Y
                      |     |
                    +-+-+ +-+-+
                    | 0 | | 1 |
                    +---+ +---+


               )
         (    (                  )      (
   )(     )    )  )           ( (   )    )  )
  (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

Note how the list b refers to the same sublists of a. (implementation detail: CPython's bytecode compiler will optimize literal expressions, so that the same 0 and 1 objects are used in both sublists. There is also some caching involved for small integers, but this is not important. In the general case the sublists do not have all elements in common).

A deep copy is a copy that avoids this sharing of identical objects.

For example, after executing:

import copy
a = [[0, 1], [0, 1]]
b = copy.deepcopy(a)

The situation is:

                ___                                              ___
               (   )                                            (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)                                            (___)
    O            ¿                                                ¿               o
                 |                                                |
       o         |                                                |
                 |                                                |
          -------+------                                   -------+------
         |  [ ¿  , ¿ ]  |                                 |  [ ¿  , ¿ ]  |
          ----|----|----                                   ----|----|----
              |     \                                          |     \
              |      \                                         |      \
              |       \                                        |       \
              |        \                                       |        \
              |         \                                      |         \
              |          \                                     |          \
              |           \                                    |           \
              |            \                                   |            \
              |             \                                  |             \
              |              \                                 |              \
              |               \                                |               \
              |                \                               |                \
         +----+----------+   +--+------------+            +----+----------+   +--+------------+
         | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |            | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |
         +----|-----|----+   +----|-----|----+            +----|-----|----+   +----|-----|----+
               \     \           /     /                        \     \           /     /
                \     \         /     /                          \     \         /     /
                 \     \       /     /                            \     \       /     /
                  \     \     /     /                              \     \     /     /
                   \     \   /     /                                \     \   /     /
                    \     | /     /                                  \     | /     /
                     |    |/     /                                    |    |/     /
                     |    X     /                                     |    X     /
                     |   / |   /                                      |   / |   /
                     |  /  |  /                                       |  /  |  /
                     \ /   \ /                                        \ /   \ /
                      Y     Y                                          Y     Y
                      |     |                                          |     |
                    +-+-+ +-+-+                                      +-+-+ +-+-+
                    | 0 | | 1 |                                      | 0 | | 1 |
                    +---+ +---+                                      +---+ +---+






               )                                               )
         (    (                  )      (                (    (                  )      (
   )(     )    )  )           ( (   )    )  )      )(     )    )  )           ( (   )    )  )
  (  )   (  ) (  (  (       (  ) ) (  ) (  (  (   (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

(Actually, it seems like copy.deepcopy is smart enough to avoid copying built-in objects that are immutable, such as int, long, tuples of immutable objects etc. so all sublists share the same 0 and 1 objects)


Note that these diagrams might also help you to understand how reference counting works. Every rope is a reference, and until an object has a chain of references that goes up to a buoy(i.e. an identifier) it stays alive. When there are no more ropes to link an object to the surface's buoys, then the objects sink, and gets destroyed by the garbage collector.

Upvotes: 6

arshajii
arshajii

Reputation: 129497

b = a[:] creates a shallow copy of a, so changing the mutable lists within b still effects those same lists in a.

In other words, a and b do not point to the same list (which is why a is not b), but rather to two different lists which both contain the same two lists. You change one of these lists via b[0][0] = 1 and that change shows up in a.

You mentioned that you were playing around with id(), so take a look at this:

>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> id(a)
2917280                    # <----+
>>> id(b)                  #      |----- different!
2771584                    # <----+
>>> id(a[0]), id(a[1])
(2917320, 2917360)         # <----+
>>> id(b[0]), id(b[1])     #      |----- same!
(2917320, 2917360)         # <----+

Upvotes: 18

Cody Piersall
Cody Piersall

Reputation: 8547

Although a is b returns False, a[0] is b[0] returns True. So when you change b[0] you are necessarily changing a[0]

>>> a = [[0,0],[0,0]]
>>> b = a[:]

>>> # a[0] is b[0] 
>>> print a[0] is b[0]
True

>>> a.append('more stuff')
>>> print a
[[0, 0], [0, 0], 'more stuff']
>>> print b
[[0, 0], [0, 0]]

Upvotes: 3

glglgl
glglgl

Reputation: 91017

In both cases you create an independent list. So a is b is always false.

In the first case, you put some other values into one of the lists.

In the second case, your lists both contain the same values.

It is as if you would write

l = []
a = [l, l]
b = [l, l]

a is not b, and nevertheless they contain the same data.

If you modify l now, this change is visible via all of a[0], a[1], b[0] and b[1].

Upvotes: 3

jh314
jh314

Reputation: 27802

a is a list of lists. When you do b=a[:], you create a new list, but copy the elements. So b is a different list, but the elements (sublists) are the same.

Upvotes: 4

Rohit Jain
Rohit Jain

Reputation: 213223

You need to make a deepcopy of your list. a[:] only makes a shallow copy - see docs

You can use copy.deepcopy function:

>>> import copy
>>> a = [[0,0],[0,0]]
>>> b = copy.deepcopy(a)
>>> b
[[0, 0], [0, 0]]
>>> b[0][0]=1
>>> a
[[0, 0], [0, 0]]

Upvotes: 13

Related Questions