ПрограммированиеBackend разработчик

Какая разница между shallow и deep copy в структуре dict, и как корректно копировать вложенные словари в Python?

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

Ответ.

История вопроса

В Python структура данных dict часто используется для хранения вложенной информации. С необходимостью клонирования таких структур часто сталкиваются разработчики при работе с шаблонами, конфигурациями или передачей данных между частями приложения.

Проблема

Стандартное копирование dict с помощью присваивания (=) создаёт лишь ссылку на исходный объект. Поверхностное копирование (shallow copy) копирует сам объект dict, но не вложенные объекты. Глубокое копирование (deep copy) рекурсивно копирует все объекты внутри, что предотвращает влияние изменений в одной копии на другую.

Решение

Для поверхностного копирования можно использовать dict.copy() или конструктор dict(), для глубокого — модуль copy и функцию deepcopy():

import copy d = {"a": 1, "b": {"c": 2}} shallow_d = d.copy() deep_d = copy.deepcopy(d) # Теперь изменение shallow_d['b']['c'] повлияет на d['b']['c'] # изменение deep_d['b']['c'] не повлияет на исходный словарь

Ключевые особенности:

  • Поверхностное копирование копирует только первую "оболочку" объекта
  • Глубокое копирование рекурсивно копирует все объекты внутри структуры
  • При работе с вложенными структурами всегда используйте deepcopy, если требуется полная независимость копии

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

Может ли dict.copy() копировать вкладки глубже первого уровня?

Нет, dict.copy() создает только поверхностную копию. Вложенные словари всё равно будут ссылками на те же объекты, что и в исходном dict.

Если в структуре есть неизменяемый объект (например, кортеж), будет ли deepcopy глубоко его копировать?

Deepcopy копирует только изменяемые вложенные объекты. Неизменяемые объекты остаются такими же — кортежи, строки и числа не копируются рекурсивно, а просто переносятся в копию.

Можно ли для глубокого копирования использовать сериализацию через json.loads(json.dumps(dict))?

Можно, но с оговорками. Такой способ работает только для сериализуемых типов и не подходит, если в словаре есть несериализуемые объекты (например функции или кастомные классы):

import json orig = {"a": 10, "b": [1,2,3]} copy_like_deep = json.loads(json.dumps(orig)) # Не работает для сложных объектов

Типовые ошибки и анти-паттерны

  • Использование простого присваивания вместо копирования
  • Применение поверхностного копирования к вложенным структурам, что приводит к неожиданным изменениям во всех "копиях"

Пример из жизни

** Негативный кейс Разработчик клонирует настройки через copy(), затем меняет вложенное значение, считая, что это две независимые структуры. Плюсы: Просто и быстро Минусы: Изменения вложенных объектов в одной копии отражаются на всех — баги, сложные для отладки. ** Позитивный кейс Разработчик всегда использует copy.deepcopy() для вложенных структур, даже если исходный dict кажется плоским. Плюсы: Гарантирована независимость данных, баги сведены к минимуму Минусы: Deepcopy медленнее и потребляет больше памяти, иногда избыточен.