2

The following code creates a list, assigns a slice of the list to a new variable, and then modifies this slice using the new variable B. The memory addresses of the elements of A and B are the same, but modifying B does not affect A. What am I missing here?

def printAddresses(A):
    for i in range(len(A)):
        print(f"memory address of list element {i}: {hex(id(A[i]))}")
    print("\n\n\n")


A = list(range(5))
B = A[:]
print("addresses of elements of A")
printAddresses(A)
print("addresses of elements of B")
printAddresses(B)

for i in range(len(B)):
    B[i]=12345
print("A: ", A)
print("B: ", B)

Output:

addresses of elements of A
memory address of list element 0: 0x10821e470
memory address of list element 1: 0x10821e490
memory address of list element 2: 0x10821e4b0
memory address of list element 3: 0x10821e4d0
memory address of list element 4: 0x10821e4f0


addresses of elements of B
memory address of list element 0: 0x10821e470
memory address of list element 1: 0x10821e490
memory address of list element 2: 0x10821e4b0
memory address of list element 3: 0x10821e4d0
memory address of list element 4: 0x10821e4f0


A:  [0, 1, 2, 3, 4]
B:  [12345, 12345, 12345, 12345, 12345]

Alex Armbruster
  • 343
  • 1
  • 2
  • 8
  • Does this answer your question? ["is" operator behaves unexpectedly with integers](https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers) –  Oct 19 '20 at 14:45
  • I think this is because B is a copy of A, not the other way around. Overwriting B doesn't work backwards, just modifies in place with a new memory address (interestingly, all the elements of B have the same memory address after overwriting) – Andrew Oct 19 '20 at 14:45
  • 1
    Does this answer your question? [What's with the integer cache maintained by the interpreter?](https://stackoverflow.com/questions/15171695/whats-with-the-integer-cache-maintained-by-the-interpreter) – Tomerikoo Oct 19 '20 at 14:45
  • This does not afaik, have anything to do with caching. Slicing creates shallow, not deep copies. – Carcigenicate Oct 19 '20 at 14:48
  • @Tomerikoo I still maintain that this question has nothing to do with caching, or immutability. They're making a shallow copy of a list, which doesn't copy the contents, but does copy the outer list. The same behavior would likely be observed by making a shallow copy of a list of mutable objects. – Carcigenicate Oct 19 '20 at 14:53
  • 1
    Y'all seem to be missing the point of the question. The OP was thinking that because the addresses of the items in the list were the same, assigning new values to one list would change the values in the other. It is the fact that there are two lists that is all that matters to the OPs question. When they change a value in one list, the matching value in the other list doesn't change. Why this is has nothing to do with what is in each list. – CryptoFool Oct 19 '20 at 15:04
  • @Steve yes this is the source of my confusion. I still need to figure out why modifications to one list do not affect the other, when the memory addresses of corresponding items across the two lists match. – Alex Armbruster Oct 19 '20 at 15:15
  • @Carcigenicate's answer is correct. When you assign something to a list, it is a node in that list that changes, not what it is currently pointing to. You're looking at the addresses of what the lists point to, not the addreses of the nodes in the list that do the pointing (which will be different). – CryptoFool Oct 19 '20 at 15:29
  • Ok after printing the memory addresses of B after assigning `B[i]=12345` for each i, I see what is going on, thanks. – Alex Armbruster Oct 19 '20 at 15:30
  • @AlexArmbruster Sorry, in retrospect, showing the IDs post re-assignment would have been a good idea. I thought my apple/orange example would have had the same effect, but I realize that that that may have been too abstract. – Carcigenicate Oct 19 '20 at 15:41

1 Answers1

1

The memory addresses of the data held by the lists are the same, since each list contains references to the same objects. A slice creates a shallow-copy, which means that the structure of the list is copied, but the objects it contains aren't. A and B both contain references to the same objects at the same time.

If you check the address of the structures themselves though, you'll see the difference:

def printAddresses(A):
    print(hex(id(A)))  # Print list address
    for i in range(len(A)):
        print(f"memory address of list element {i}: {hex(id(A[i]))}")
    print("\n\n\n")

Then, when run:

addresses of elements of A
0x420cd88
memory address of list element 0: 0x63abf7a0
memory address of list element 1: 0x63abf7b0
memory address of list element 2: 0x63abf7c0
memory address of list element 3: 0x63abf7d0
memory address of list element 4: 0x63abf7e0
addresses of elements of B
0x195d0a8
memory address of list element 0: 0x63abf7a0
memory address of list element 1: 0x63abf7b0
memory address of list element 2: 0x63abf7c0
memory address of list element 3: 0x63abf7d0
memory address of list element 4: 0x63abf7e0
A:  [0, 1, 2, 3, 4]
B:  [12345, 12345, 12345, 12345, 12345]

Note how 0x420cd88 != 0x195d0a8. The objects held are literally the same, but the structures holding them are different.

I think a key here is that B[i]=12345 does not modify the object that was previously at i. B[0]=12345 removes the 0 from the list, then puts a 12345 in its place. That does not change the 0.

It's the same as how this:

n = 0
list_a = [n]
list_b = [n]  # Same object in both lists

list_a[0] = 9
print(list_a, list_b)

Prints [9] [0]. This takes the 0 out of list_a, and puts a 9 in its place. The 0 is not modified. Taking an orange of a box and putting an apple in its place does not modify the orange.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Ok the addresses of the structures holding `A` and `B` are different, but they both seem to contain pointers to the same set of five elements, so I don't see why the final two lines printed aren't A: [12345, 12345, 12345, 12345, 12345] B: [12345, 12345, 12345, 12345, 12345] – Alex Armbruster Oct 19 '20 at 15:01
  • @AlexArmbruster Because `B[i]=12345` modifies the copy `B` of the structure, and alters what object is in `B`. `[i]=` (or rather, `__setitem__`) takes out whatever was at position `i`, and puts a new object in its place. That does not effect what was at `i` previously. If I take an orange out of a box and replace it with an apple, that doesn't modify the orange in any way. – Carcigenicate Oct 19 '20 at 15:07
  • @AlexArmbruster There are *copies of references* to the same object, not copies of objects. If you know C/C++, you can think of the lists as being filled with pointers to the same objects. Both independent lists contain literally the same objects; not copies of objects. I'll fix my first paragraph to clarify. – Carcigenicate Oct 19 '20 at 15:24