Вопрос с собеседования · Python

Чем поверхностная копия отличается от глубокой?

JuniorПамять и копирование
Ещё спрашивают так:
  • В чём разница между copy и deepcopy?
  • Как правильно скопировать вложенный список?
  • Почему изменение копии меняет оригинал?
Коротко

Поверхностная копия создаёт новый контейнер со старыми элементами, глубокая — копирует всё рекурсивно.

  • Поверхностная: copy.copy(x), x[:], list(x), dict(x), x.copy()
  • Глубокая: copy.deepcopy(x) — вложенные объекты тоже новые

А ещё Присваивание b = a не копирует вообще — это второе имя того же объекта

Подробно

Три разных действия:

  • присваивание b = a — копии нет, оба имени указывают на один объект;
  • поверхностная копия — новый внешний объект, но внутри те же ссылки на вложенные объекты. Изменение самого списка копии оригинал не трогает, а изменение вложенного списка видно в обоих;
  • глубокая копия — copy.deepcopy рекурсивно копирует вложенные объекты. Она отслеживает уже скопированное (словарь memo), поэтому корректно работает с циклическими ссылками и общими подобъектами.

Неизменяемые объекты (числа, строки, кортежи из неизменяемых) не копируются даже глубоко — в этом нет смысла.

Глубокая копия медленнее и не всегда возможна: файлы, сокеты, блокировки не копируются. Свои классы могут управлять копированием через __copy__ и __deepcopy__.

Частый практический случай — матрица: [[0] * 3] * 3 создаёт три ссылки на один и тот же внутренний список. Правильно — [[0] * 3 for _ in range(3)].

Пример

import copy

a = [[1, 2], [3, 4]]
shallow = copy.copy(a)
deep = copy.deepcopy(a)

a[0].append(99)
shallow[0]   # [1, 2, 99] — вложенный список общий
deep[0]      # [1, 2]     — независимая копия

grid = [[0] * 3] * 3
grid[0][0] = 1
grid         # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] — одна строка трижды

Что хотят услышать

  • Три уровня: присваивание, поверхностная и глубокая копия.
  • Способы сделать каждую копию.
  • Пример с вложенным изменяемым объектом.
  • Ловушка с [[0] * n] * m — плюс.

Частые ошибки

  • Считать b = a копией.
  • Думать, что a[:] или list(a) копирует вложенные списки.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Поверхностная копия рекурсивно копирует вложенные списки, а deepcopy ещё и неизменяемые объекты
  • ✓Поверхностная копирует только контейнер, deepcopy — всё рекурсивно
  • ✗Присваивание b = a создаёт поверхностную копию списка, а copy.copy(a) — уже глубокую
  • ✗list(x) и срез x[:] создают глубокую копию со всеми вложенными объектами

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования