Для чего нужен модуль `copy` в Python и в чём разница между `copy.copy()` и `copy.deepcopy()`?

Ответ

Модуль copy в Python используется для создания копий изменяемых объектов (списков, словарей, пользовательских классов). Прямое присваивание (new_list = old_list) создаёт лишь новую ссылку на тот же объект, а модуль copy позволяет получить отдельный, независимый объект.

Ключевые функции:

  1. copy.copy(x) (Поверхностное копирование): Создаёт новый составной объект, но затем вставляет в него ссылки на объекты, найденные в оригинале. Вложенные изменяемые объекты по-прежнему являются общими.
  2. copy.deepcopy(x) (Глубокое копирование): Рекурсивно создаёт новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале. Полностью независимая копия всей иерархии объектов.

Практический пример, демонстрирующий разницу:

import copy

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

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

# Изменяем элемент во вложенном списке оригинала
original[2][0] = 'X'

print('Original:', original)  # [1, 2, ['X', 4]]
print('Shallow copy:', shallow) # [1, 2, ['X', 4]] -> Изменился!
print('Deep copy:', deep)      # [1, 2, [3, 4]]    -> Остался неизменным

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

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

Важно: Глубокое копирование может быть ресурсоёмким для очень больших или циклических структур.

Ответ 18+ 🔞

Да ты посмотри, какой народ безмозглый, блядь! Сидят, думают, что new_list = old_list — это копию сделало. Да хуй там плавал, ёпта! Это ж просто новую табличку с названием прилепили к той же самой, блядь, коробке с данными! Поменял в одном месте — везде пиздец, всё поплыло!

А вот модуль copy — это уже, блядь, серьёзные люди придумали. Он как раз для таких распиздяев, которые не хотят нечаянно всё похерить.

Чем там богаты, блядь:

  1. copy.copy(x) (Поверхностная копия): Ну, это как взять новый шкаф, но все ящики из старого тупо переставить в него. Сам шкаф-то новый, а ящики — те же самые, общие. Короче, верхний уровень скопировал, а внутрь заглядывать не стал, ленивая жопа.
  2. copy.deepcopy(x) (Глубокая копия): А вот это, сука, перфекционизм! Берёт шкаф, вытряхивает все ящики, а потом каждый ящик, блядь, заново собирает из новых досок! И так до самого низа, пока всё, сука, не склонирует. Полная независимость, мать его.

Смотри сюда, на живом примере, а то не дойдёт:

import copy

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

shallow = copy.copy(original)  # Поверхностно скопировали
deep = copy.deepcopy(original) # Глубоко, с чувством, с толком, с расстановкой

# Теперь в оригинале лезем во внутренний список и хуяк — меняем
original[2][0] = 'X'

print('Original:', original)  # [1, 2, ['X', 4]] — логично
print('Shallow copy:', shallow) # [1, 2, ['X', 4]] — ОЙ БЛЯДЬ! И тут поменялось! Потому что ящик-то общий!
print('Deep copy:', deep)      # [1, 2, [3, 4]]    — А тут нихуя! Герой! Отдельная вселенная!

Так когда же что брать, чтоб не обосраться?

  • deepcopy: Когда тебе нужна, блядь, ТОЧНАЯ и НЕЗАВИСИМАЯ копия всей этой иерархии. Собираешься ковыряться глубоко внутри и не хочешь, чтобы оригинал ахуел от твоих экспериментов. Для конфигов, графов — идеально.
  • copy: Когда объект простой, одноэтажный, или когда вложенные штуки неизменяемые (как кортежи), или когда тебе похуй и ты сознательно хочешь, чтобы они были общими (ну, для экономии памяти там).

И да, deepcopy — он мощный, но если скопировать им структуру, которая сама на себя ссылается, или просто овердохуища большую, то можно и подождать немного. Так что без фанатизма, ёпта.