Развернутое объяснение
Для изменяемых структур (списки, словари, вложенные коллекции) важно различать:
простое присваивание
b = a— не копия, обе переменные указывают на один и тот же объект;поверхностная копия — копируется только внешний контейнер;
глубокая копия — копируется и контейнер, и все вложенные объекты.
Поверхностная копия (shallow copy)
При поверхностной копии создаётся новый контейнер, но его элементы — это те же объекты, что и внутри оригинала.
Способы сделать shallow copy:
import copy
a = [[1, 2], [3, 4]]
b = a.copy() # для списка
# или
b = list(a)
# или
b = copy.copy(a)Поведение:
a = [[1, 2], [3, 4]]
b = a.copy()
b[0][0] = 99
print(a) # [[99, 2], [3, 4]]
print(b) # [[99, 2], [3, 4]]Мы создали новый список b, но внутренние списки те же самые, поэтому изменение вложенного элемента отражается и в a.
Если же переопределить элемент верхнего уровня — поведение уже другое:
a = [[1, 2], [3, 4]]
b = a.copy()
b[0] = [99, 2]
print(a) # [[1, 2], [3, 4]]
print(b) # [[99, 2], [3, 4]]Здесь мы заменили ссылку в b[0] на новый список — оригинал не изменился.
Глубокая копия (deep copy)
При глубокой копии создаётся новый контейнер и рекурсивные копии всех вложенных объектов, так что копия полностью независима от оригинала.
Создание:
import copy
a = [[1, 2], [3, 4]]
c = copy.deepcopy(a)
c[0][0] = 99
print(a) # [[1, 2], [3, 4]]
print(c) # [[99, 2], [3, 4]]Изменения в c не затрагивают a, потому что вложенные списки тоже скопированы.
Как объяснить на собеседовании?
Поверхностная копия создаёт новый контейнер, но вложенные объекты остаются общими — копируются только ссылки. Поэтому изменение вложенных элементов видно и в оригинале, и в копии. Глубокая копия рекурсивно копирует всю структуру: и контейнер, и все вложенные объекты. В результате копия полностью независима, и изменения в ней не влияют на исходные данные.