1

According to this, python copies references when slicing. I've tried the following:

>>> a=[1,2,3]
>>> b=a[:]
>>> b[1]=0
>>> a
[1, 2, 3]
>>> b
[1, 0, 3]
>>> map(id,a)
[14508376, 14508352, 14508328]
>>> map(id,b)
[14508376, 14508400, 14508328]

Why does b[1]=0 does not change a[1] (which should be the case if b[1] was indeed a reference to the same object, one might think)? Instead, it seems to generate a new reference/id and change the new object. Anywhere I can read up on this behavior in more detail?

DeLorean88
  • 547
  • 2
  • 5
  • 15
  • Assignment copies references too. See https://nedbatchelder.com/text/names.html. – user2357112 Jul 10 '18 at 20:54
  • 2
    `b[1]=0` changes the reference, not the referred-to object. So now the two lists have different references. When you see changes reflected across lists, it's not because the change in a reference in `b` changes the reference in `a`, it's because the object that is referred to in both `a` and `b` had changed. Draw some pictures with arrows representing the references, it really does help. – Patrick Haugh Jul 10 '18 at 20:57
  • @user2357112 yes, but the behavior is different – DeLorean88 Jul 10 '18 at 21:00
  • Different from what? It's certainly not different from the behavior of copying references, if that's what you're thinking. – user2357112 Jul 10 '18 at 21:01
  • @user2357112 I meant `c=a` (assignment), `c[1]=0` result in `a` being `[1,0,3]`, unlike when using slice `a[:]` – DeLorean88 Jul 10 '18 at 21:06
  • @DeLorean88: Slicing copies the list and all the references inside it. `c=a` just copies the reference to the list. – user2357112 Jul 10 '18 at 21:09

1 Answers1

5

Suppose you start with a = [1,2,3]. In Python's data model, this means that a refers to an object in memory:

a -> [ * | * | * ]
       |   |   |
       v   v   v
       1   2   3

With b = a, you simply point another name at the same object:

a -> [ * | * | * ] <- b
       |   |   |
       v   v   v
       1   2   3

b[1] = 0 changes the same reference a[1] = 0 would:

           0
           ^
           |
a -> [ * | * | * ] <- b
       |       |
       v       v
       1   2   3

(The 2 is still in memory, possibly referenced directly or indirectly via some other name, but not via a or b anymore.)


With b = a[:], you create a new list, but that new list contains references to the same object:

a -> [ * | * | * ]
       |   |   |
       v   v   v
       1   2   3
       ^   ^   ^
       |   |   |
b -> [ * | * | * ]

Now when you write b[1] = 0, you aren't changing a[1], because a and b are distinct list objects.

a -> [ * | * | * ]
       |   |   |
       v   v   v
       1   2   3
       ^       ^
       |       |
b -> [ * | * | * ]
           |
           v
           0

Ned Batchelder's blog post (and subsequent PyCon talk) is an excellent overview of Python's name model.

chepner
  • 497,756
  • 71
  • 530
  • 681