Практическая ситуация: после поверхностного копирования вложенного списка изменение внутреннего списка отражается в оригинале. Какой механизм Python это объясняет?
Поверхностная копия создаёт новый внешний контейнер, но сохраняет ссылки на те же вложенные объекты. Поэтому изменение вложенного списка видно через оба контейнера. Для независимой копии всей структуры нужен механизм глубокого копирования.
В Python переменные хранят ссылки на объекты, а контейнеры состоят из ссылок на другие объекты. Такой подход позволяет эффективно передавать и копировать большие структуры без автоматического дублирования каждого вложенного объекта.
Для явного управления копированием стандартная библиотека предоставляет модуль copy, разделяющий поверхностное и глубокое копирование. Это позволяет выбирать между производительностью и независимостью копии.
Поверхностная копия безопасно отделяет внешний контейнер: добавление нового элемента в копию не изменяет исходный список. Однако уже существующие вложенные изменяемые объекты остаются общими.
Если разработчик считает поверхностную копию полностью независимой, изменения могут незаметно попасть в исходные данные. Это особенно опасно при подготовке конфигураций, состоянии объектов, данных тестов и значениях по умолчанию.
При поверхностном копировании Python создаёт новый объект верхнего уровня и переносит в него ссылки на элементы исходного контейнера. Неизменяемые объекты обычно не создают практической проблемы, а общие изменяемые объекты позволяют менять состояние сразу через несколько путей доступа.
В примере shallow и original содержат одну и ту же первую вложенную строку-список, поэтому её изменение отражается в обоих объектах. deep рекурсивно копирует вложенные объекты, поэтому изменение его данных не затрагивает original.
Глубокое копирование не всегда является универсальным решением. Оно может быть дорогим, создавать больше объектов и иметь проблемы с ресурсами, рекурсивными структурами или объектами, которые нельзя корректно копировать. Кроме того, глубокая копия не заменяет продуманную модель данных: иногда лучше явно создавать новый объект или использовать неизменяемые структуры.
Сервис хранил шаблон настроек с вложенными списками. Разработчик делал поверхностную копию шаблона для каждого запроса, добавлял в один из вложенных списков параметры запроса, и постепенно менялся общий шаблон. В результате последующие запросы получали данные предыдущих.
Рассматривались три варианта. Ручное копирование отдельных вложенных списков было быстрым, но хрупким: при изменении структуры легко забыть новый уровень. Глубокое копирование устраняло общие ссылки, но увеличивало стоимость обработки больших настроек. Использование неизменяемой модели требовало более заметной переработки кода, зато снижало риск случайных изменений.
Для небольших конфигураций выбрали copy.deepcopy, потому что корректность была важнее небольшой дополнительной стоимости. Для больших объектов позднее перешли к явному созданию только изменяемых частей, что уменьшило потребление памяти и сделало границы владения данными очевиднее.
Вопрос: Изменяет ли добавление элемента во внешний контейнер через поверхностную копию исходный контейнер?
Ответ: Нет. Внешний контейнер у копии новый, поэтому добавление или удаление элемента меняет только копию. Общими остаются вложенные объекты, на которые были перенесены ссылки.
Вопрос: Всегда ли глубокое копирование создаёт полностью независимую копию каждого объекта?
Ответ: Нет. Механизм копирования использует специальные правила объектов и может сохранять некоторые объекты общими, если их копирование не имеет смысла или не поддерживается. Поэтому для пользовательских классов следует проверять и при необходимости явно определять поведение копирования.
Вопрос: Почему неизменяемый вложенный объект обычно не создаёт проблемы при поверхностной копии?
Ответ: Общая ссылка на неизменяемый объект безопасна, поскольку его состояние нельзя изменить на месте. Операция, выглядящая как изменение, обычно создаёт новый объект и переназначает ссылку в конкретном контейнере, не затрагивая остальные ссылки на старый объект.