Копирование: ссылка, shallow copy и deep copy

Классический вопрос: чем отличаются присваивание, поверхностная и глубокая копия и когда вложенные данные «протекают».

Shallow copy копирует только верхний уровень контейнера, а вложенные объекты остаются общими. Deep copy рекурсивно копирует всё дерево объектов.

Три уровня «копирования»

Чёткий ответ. b = a — это не копия, а второе имя того же объекта. copy.copy(a) делает новый внешний контейнер, но вложенные объекты те же. copy.deepcopy(a) делает полностью независимую копию.

import copy

orig = [[1, 2], [3, 4]]
shallow = copy.copy(orig)        # копия верхнего уровня
deep = copy.deepcopy(orig)       # полностью независимая копия

orig[0].append(99)               # меняем вложенный список

print("orig:   ", orig)
print("shallow:", shallow)       # увидел изменение — вложенный список общий
print("deep:   ", deep)          # независим

Вывод:

orig:    [[1, 2, 99], [3, 4]]
shallow: [[1, 2, 99], [3, 4]]
deep:    [[1, 2], [3, 4]]

Поверхностная копия «увидела» добавление 99, потому что внутренний список [1, 2] у orig и shallow — один и тот же объект. Глубокая копия скопировала и его, поэтому осталась нетронутой.

Способы сделать поверхностную копию

Для списка их несколько, и все эквивалентны copy.copy:

a = [1, 2, 3]
print(a[:])           # срез
print(list(a))        # конструктор
print(a.copy())       # метод copy

b = a.copy()
b.append(4)
print("a:", a)        # оригинал не тронут (верхний уровень)
print("b:", b)

Вывод:

[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
a: [1, 2, 3]
b: [1, 2, 3, 4]

Когда нужна именно глубокая копия

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

import copy
config = {"users": ["Аня", "Боб"], "limit": 10}
backup = copy.deepcopy(config)
config["users"].append("Ева")
print("config:", config["users"])
print("backup:", backup["users"])   # резерв не изменился

Вывод:

config: ['Аня', 'Боб', 'Ева']
backup: ['Аня', 'Боб']

Итог

  • b = a — не копия, а ещё одно имя того же объекта.
  • copy.copy копирует верхний уровень; вложенные объекты общие.
  • copy.deepcopy рекурсивно копирует всё — нужен при вложенных изменяемых данных.
Проверьте себя
1. Что делает copy.copy для вложенного списка?
AКопирует всё рекурсивно
BКопирует только верхний уровень, вложенное остаётся общим
CНичего не копирует
DТо же, что deepcopy
2. Когда обязательно нужен deepcopy?
AВсегда
BДля плоских списков чисел
CКогда есть вложенные изменяемые объекты и копию надо менять независимо
DДля строк
3. Что из этого НЕ создаёт копию списка a?
Aa[:]
Blist(a)
Ca.copy()
Db = a
Поддержать проект