Для изменяемых структур (списки, словари, вложенные коллекции) важно различать:
простое присваивание 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, потому что вложенные списки тоже были скопированы.
Формулировка для собеседования
Поверхностная копия создаёт новый контейнер, но вложенные объекты остаются общими — копируются только ссылки, поэтому изменение вложенных элементов видно и в оригинале. Глубокая копия рекурсивно копирует всю структуру, и получившаяся копия полностью независима: изменения в ней не влияют на исходные данные.