What is happening when you modify a slice of a python list

Viewed 362

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]

1 Answers

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.

Related