ПрограммированиеData Engineer

Поясните разницу между shallow copy (поверхностным копированием) и deep copy (глубоким), а также поведением copy.copy и copy.deepcopy при копировании сложных объектов. Приведите пример, где неправильное понимание привело к багу.

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Поверхностная копия (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): полностью рекурсивное копирование
  • Некоторые объекты (например, открытые файлы, сокеты) deep copy не копируют (исключение)

Вопрос с подвохом.

Что произойдёт при копировании кортежа, который содержит изменяемые объекты?

Ответ с примером:

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.