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