В чем разница между copy и deepcopy в Python?

Ответ

Разница заключается в том, как копируются вложенные (составные) объекты. Обе функции находятся в модуле copy.

copy.copy(x) (Поверхностное копирование):

  • Создает новый составной объект (например, новый список или словарь).
  • Затем вставляет ссылки на объекты, найденные в оригинале. Если элемент оригинала — это список, в копию попадет ссылка на тот же самый список, а не его копия.
  • Изменения во вложенных изменяемых объектах оригинала отразятся на копии, и наоборот.

copy.deepcopy(x) (Глубокое копирование):

  • Создает новый составной объект.
  • Затем рекурсивно вставляет копии объектов, найденных в оригинале. Копируется весь объект «вглубь».
  • Полностью независимая копия. Изменения в любой части оригинала или копии не затрагивают друг друга.

Практические примеры:

import copy

# Исходная структура с вложенным списком
original = [1, 2, [3, 4]]

# Поверхностное копирование
shallow_copied = copy.copy(original)
# Глубокое копирование
deep_copied = copy.deepcopy(original)

# Модифицируем вложенный список в оригинале
original[2].append(5)

print("Original:", original)      # [1, 2, [3, 4, 5]]
print("Shallow copy:", shallow_copied) # [1, 2, [3, 4, 5]] -> изменился!
print("Deep copy:", deep_copied)    # [1, 2, [3, 4]] -> остался неизменным

Когда что использовать:

  • copy.copy(): Когда объект состоит только из неизменяемых типов (числа, строки, кортежи) или когда вам намеренно нужны общие ссылки на вложенные изменяемые объекты (редкий случай).
  • copy.deepcopy(): Когда вам нужна полная, независимая копия сложной структуры данных (списки списков, словари, содержащие другие словари, пользовательские объекты). Это безопасный, но более ресурсоемкий вариант.

Важно: deepcopy может корректно обрабатывать рекурсивные ссылки и пользовательские объекты, если они определяют методы __copy__() или __deepcopy__().

Ответ 18+ 🔞

А, ну это классика, ёпта! Сидишь такой, пишешь код, думаешь — всё, я гений, сейчас скопирую список и буду с ним работать. А потом бац — и нихуя не работает, потому что ты не скопировал, а просто на тот же самый список ещё одну бумажку с названием прилепил. Вот чтобы такого не было, есть модуль copy с двумя главными героями.

copy.copy(x) (Поверхностное копирование, оно же shallow copy): Это как взять папку с документами, сделать её точную копию, но внутри лежат не копии документов, а ссылочки на оригинальные бумажки. Новую папку создал? Создал. А тронешь какую-нибудь бумажку внутри — охуеть, она и в оригинальной папке тоже меняется! Потому что это одна и та же бумажка, блядь. Короче, копирует только верхний слой, а всё, что внутри — там просто ссылки остаются.

copy.deepcopy(x) (Глубокое копирование): А вот это уже серьёзный подход, ядрёна вошь! Тут уже не ссылки, а полная, тотальная репликация всего и вся. Берёшь папку, копируешь, потом залезаешь внутрь, каждую бумажку копируешь, потом если в бумажке вложенные листочки — их тоже копируешь, и так до бесконечности. Получается абсолютно независимый клон. Что с ним ни делай — на оригинал это нихуя не повлияет. Мощно, но и ресурсов жрёт овердохуища, если структура большая.

Смотри, как это на практике выглядит:

import copy

# Допустим, у тебя есть список, а внутри него ещё один список. Классика.
original = [1, 2, [3, 4]]

# Делаем поверхностную копию
shallow_copied = copy.copy(original)
# Делаем глубокую копию
deep_copied = copy.deepcopy(original)

# Теперь начнём хулиганить. Добавим что-нибудь во вложенный список оригинала.
original[2].append(5)

print("Original:", original)      # [1, 2, [3, 4, 5]] — логично
print("Shallow copy:", shallow_copied) # [1, 2, [3, 4, 5]] — охуеть! Он тоже изменился!
print("Deep copy:", deep_copied)    # [1, 2, [3, 4]] — а вот этот стоик, блядь, ни в одном глазу. Независимый.

Так когда же что юзать, чувак?

  • copy.copy(): Когда ты уверен, что у тебя структура простая и вся из неизменяемого барахла (циферки, строки, кортежи). Или когда тебе нахуй не нужна независимая копия, а нужны именно общие ссылки (редкий, но бывает случай). Доверия к этой функции, блядь, ноль, если не знаешь, что внутри объекта.
  • copy.deepcopy(): Когда у тебя там адский замес — список списков, словарь словарей, да ещё и свои объекты кастомные. Когда нужна полная, стопроцентная изоляция. Это как переезд в отдельную квартиру, а не просто смена таблички на двери. Безопасно, но если данных много — может всё зависнуть, будь готов.

И последнее, но важное: deepcopy — он умный, ёпта. Он даже с рекурсивными ссылками (когда объект ссылается сам на себя) справится и не уйдёт в бесконечный цикл. А если у твоего объекта есть специальные методы __copy__() или __deepcopy__(), то он и их учтёт. В общем, инструмент на все случаи жизни, но тяжёлый.