Ответ
Разница заключается в том, как копируются вложенные (составные) объекты. Обе функции находятся в модуле 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__(), то он и их учтёт. В общем, инструмент на все случаи жизни, но тяжёлый.