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

«В чем разница между copy и deepcopy в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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