Поверхностная копия (shallow copy) создаёт новый объект верхнего уровня, но вложенные объекты (элементы списков, вложенные словари и т.д.) делят память с оригиналом. Глубокая копия (deep copy) рекурсивно копирует все уровни объектов.
Использовать можно через модуль copy:
import copy original = [[1, 2], [3, 4]] shallow = copy.copy(original) deep = copy.deepcopy(original) shallow[0][0] = 99 # Меняет и original, и shallow print(original) # [[99, 2], [3, 4]] # Теперь с deep original = [[1, 2], [3, 4]] deep = copy.deepcopy(original) deep[0][0] = 99 print(original) # [[1, 2], [3, 4]]
copy.copy(obj): копирует только "обёртку"copy.deepcopy(obj): полностью рекурсивное копированиеЧто произойдёт при копировании кортежа, который содержит изменяемые объекты?
tuple неизменяем, но его элементы могут быть изменяемыми (например, список). Shallow copy и deep copy отличаются:
import copy t = ([1, 2], [3, 4]) shallow = copy.copy(t) deep = copy.deepcopy(t) shallow[0][0] = 99 print(t) # ([99, 2], [3, 4]) # При deep copy: t = ([1, 2], [3, 4]) deep = copy.deepcopy(t) deep[0][0] = 88 print(t) # ([1, 2], [3, 4])
История
В приложении для обработки документов данные хранили в списке внутри словаря. При копировании объектов с помощью copy.copy вложенные элементы случайно изменялись при модификации нового объекта. В итоге поменялись исходные данные в других частях системы! Произошёл data corruption. Исправили только заменой на deepcopy.
История
В проекте по машинному обучению при сохранении гиперпараметров моделей копировали список конфигураций через стандартное присваивание и shallow copy, после чего разные эксперименты взаимно меняли свои параметры. Источник бага нашли после диагностики поведения копирования вложенных списков через deepcopy.
История
В финансовом ПО deep copy случайно применили к объекту, который включает открытый файл (handle), что вызвало TypeError: cannot pickle '_io.TextIOWrapper' object — потому что copy.deepcopy под капотом использует pickle. Исправили через явную обработку таких полей или вызов shallow copy.