CollectionsChapter 32 of 114
Copy Lists
Why assignment does not copy, and the difference between shallow and deep.
Assignment does not copy
This is the single most common list surprise. = gives the same list another name:
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)
print(a is b)Output
[1, 2, 3, 4] [1, 2, 3, 4] True
One list, two names. is confirms it.
Three ways to copy
All of these build a new list:
original = [1, 2, 3]
by_slice = original[:]
by_method = original.copy()
by_call = list(original)
by_slice.append(99)
print(original)
print(by_slice, by_method, by_call)
print(by_slice is original)Output
[1, 2, 3] [1, 2, 3, 99] [1, 2, 3] [1, 2, 3] False
copy() says what it means, so prefer it. [:] is common in older code, and list() is handy when you are converting something else anyway.
Shallow means one level deep
All three copy the outer list. The items inside are still shared, which only shows when those items are themselves changeable:
grid = [[1, 2], [3, 4]]
shallow = grid.copy()
shallow[0].append(99)
print(grid)Output
[[1, 2, 99], [3, 4]]
The outer lists are separate, but shallow[0] and grid[0] are the same inner list. Appending through one shows through the other.
Adding to the outer list behaves as you would expect:
grid = [[1, 2]]
shallow = grid.copy()
shallow.append([5, 6])
print(grid)
print(shallow)Output
[[1, 2]] [[1, 2], [5, 6]]
deepcopy goes all the way down
from copy import deepcopy
grid = [[1, 2], [3, 4]]
deep = deepcopy(grid)
deep[0].append(99)
print(grid)
print(deep)Output
[[1, 2], [3, 4]] [[1, 2, 99], [3, 4]]
deepcopy rebuilds every nested object, so nothing is shared. It costs more time and memory, so use it when you need it rather than by default.
When shallow is enough
If the items cannot be changed — numbers, strings, tuples — a shallow copy is a full copy for every practical purpose:
names = ["Ada", "Grace"]
copy = names.copy()
copy[0] = "Alan"
print(names)
print(copy)Output
['Ada', 'Grace'] ['Alan', 'Grace']
Assigning to copy[0] replaces the item rather than changing it, so the original is untouched.
Copying is not always the answer
Often the cleanest fix is to build a new list rather than copy and edit one:
prices = [10, 20, 30]
discounted = [p * 0.9 for p in prices]
print(prices)
print(discounted)Output
[10, 20, 30] [9.0, 18.0, 27.0]
Test yourself
2 questionsWhat does b = a do for a list a?
Show the answer
Gives the same list a second name — Use a.copy(), a[:] or list(a) to actually make a new list.
After shallow = grid.copy(), what does shallow[0].append(99) do to grid?
Show the answer
grid[0] gets 99 too, because the inner lists are shared — A shallow copy duplicates the outer list only. deepcopy rebuilds every level.
Join Lists
Put two lists together, and turn a list into a string.