Чем list.copy() и срез [:] отличаются от copy.deepcopy для вложенных списков?
Скопировал список через b = a[:], поменял элемент во вложенном списке b[0][0] = 99 — и a тоже изменился! Думал, срез делает полную копию. В чём дело и когда нужен deepcopy?
2 ответа
И a[:], и a.copy(), и list(a) делают одно и то же — поверхностную (shallow) копию. Создаётся новый внешний список, но его элементы — это те же самые объекты, что и в оригинале. Если внутри лежат вложенные списки, обе копии ссылаются на одни и те же вложенные списки, поэтому изменение b[0][0] видно и через a.
import copy
a = [[1, 2], [3, 4]]
b = a[:] # shallow
b[0][0] = 99
print(a) # [[99, 2], [3, 4]] — оригинал тоже изменился!
c = copy.deepcopy(a) # рекурсивно копирует и вложенные
c[0][0] = 0
print(a) # без изменений
Правило: для плоского списка из чисел/строк хватает среза или .copy(). Как только есть вложенность и вы собираетесь её менять — нужен copy.deepcopy.
Добавлю: b[0][0] = 99 мутирует общий вложенный список, поэтому видно в обоих. А вот b[0] = [99, 2] заменил бы ссылку только в b, и оригинал бы не пострадал — потому что внешний-то список у копии свой. Тонкость, но важная.