Чем отличается поверхностное копирование от глубокого? Когда какое использовать?
Короткий ответ
- Присваивание копию не создаёт — только новую ссылку
- copy.copy() копирует верхний уровень, вложенные объекты общие
- copy.deepcopy() рекурсивно копирует всю структуру
- Срез list[:] и dict.copy() — поверхностные копии
- deepcopy дороже и справляется с циклическими ссылками
- Для плоских структур достаточно shallow-копии
Поверхностная копия дублирует только верхний контейнер и делит вложенные объекты с оригиналом, глубокая — рекурсивно копирует всё.
Как сказать вслух
пример ответаСначала важно, что присваивание вообще ничего не копирует — обе переменные указывают на один объект. Поверхностная копия создаёт новый контейнер, но вложенные объекты остаются общими: изменил вложенный список в копии — изменился и оригинал. Глубокая копия рекурсивно дублирует всю структуру, поэтому объекты полностью независимы, но это дороже. Для плоского списка чисел хватит среза, для вложенных структур нужен deepcopy.
Подробный ответ
Основной ответ
Присваивание b = a создаёт вторую ссылку на тот же объект. Поверхностная копия (copy.copy(), list(a), a[:], dict.copy()) создаёт новый контейнер того же типа, но элементы — те же самые объекты: для неизменяемых элементов это безопасно, для вложенных списков и словарей изменения «просвечивают» в оригинал. Глубокая копия (copy.deepcopy()) рекурсивно копирует все уровни, отслеживая уже скопированные объекты через memo-словарь — поэтому корректно обрабатывает циклические ссылки, но заметно дороже по времени и памяти. Классы могут управлять копированием через __copy__ и __deepcopy__. Практическое правило: плоская структура или элементы неизменяемы — shallow; вложенные изменяемые данные, которые надо изолировать, — deep.
Ключевые моменты
- Три уровня. Присваивание — одна ссылка; shallow — новый контейнер, общие элементы; deep — независимая структура целиком.
- Когда shallow опасна. Матрица как список списков: копия через [:] делит строки с оригиналом, правка ячейки видна в обоих.
- Цена deepcopy. Рекурсивный обход всей структуры медленный; на больших графах объектов это узкое место.
- Кастомизация. __copy__/__deepcopy__ позволяют, например, не копировать соединение с БД внутри объекта.
Практический контекст
Типичный рабочий баг: функция «скопировала» конфиг через dict.copy() и изменила вложенную секцию — изменился общий конфиг. На собеседовании это проверяют вопросом «что выведет код» с вложенным списком. Интервьюер хочет услышать модель ссылок, а не заученные названия функций; плюсом будет упоминание memo и __deepcopy__.
Пример кода
import copy
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]
deep = copy.deepcopy(matrix)
shallow[0][0] = 99
print(matrix[0][0]) # 99 — строка общая с копией
print(deep[0][0]) # 1 — глубокая копия независимаЧастые ошибки
- Думают, что b = a создаёт копию списка
- Используют [:] для вложенных структур и получают общие внутренние списки
- Применяют deepcopy ко всему подряд, не думая о стоимости