Поверхностное и глубокое копирование в Python
Перевод статьи Python Shallow Copy and Deep Copyopen in new window.
Копирование объектов в Python
Оператор присваивания =
не создаёт копию объекта. Присваивание создаёт новую переменную которая дублирует ссылку на исходный объект.
Рассмотрим пример
old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 'a']]
new_list = old_list
new_list[2][2] = 9
print('Old List:', old_list)
print('ID of Old List:', id(old_list))
print('New List:', new_list)
print('ID of New List:', id(new_list))
2
3
4
5
6
7
8
9
10
После запуска программа выдаст следующее:
Old List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ID of Old List: 140673303268168
New List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ID of New List: 140673303268168
2
3
4
5
Обе переменные ссылаются на один объект, это подтверждает одинаковый идентификатор объекта 140673303268168. Так что при изменении значений в переменных new_list
и old_list
, изменение будет видно в обоих переменных.
Иногда нужно оставить исходное значение без изменений и модифицировать только новое. В Python, есть два способа сделать копию: поверхностное копирование и глубокое копирование.
Модуль Copy
Модуль copy
используется для поверхностного и глубокого копирования. Например:
import copy
copy.copy(x)
copy.deepcopy(x)
2
3
4
Метод copy()
возвращает поверхностную копию x
, а метод deepcopy()
возвращает глубокую копию x
.
Поверхностное копирование
Поверхностное копирование создаёт новый объект и копирует ссылки на все вложенные объекты. Это означает что процесс копирования не рекурсивный и не создаёт копий вложенных объектов.
import copy
old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_list = copy.copy(old_list)
print("Old list:", old_list)
print("New list:", new_list)
2
3
4
5
6
7
Вывод программы:
Old list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2
В программе выше, мы создали вложенный список и его поверхностную копию методом copy()
. Это означает что мы создали независимый объект с тем же содержимым.
Чтобы проверить что объекты разные добавим в один из списков новый элемент:
import copy
old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.copy(old_list)
old_list.append([4, 4, 4])
print("Old list:", old_list)
print("New list:", new_list)
2
3
4
5
6
7
8
9
Вывод программы:
Old list: [[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
2
Изменения в исходном списке (old_list
) не повлияли на новый список (new_list
).
Попробуем изменить один из вложенных объектов:
import copy
old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.copy(old_list)
old_list[1][1] = 'AA'
print("Old list:", old_list)
print("New list:", new_list)
2
3
4
5
6
7
8
9
Вывод программы:
Old list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]
2
Мы изменили вложенный в в оригинальный объект список (old_list[1]
). Так как ссылки на вложенные элементы в двух списках одинаковые, изменение вложенного списка отразилось и на old_list
и на new_list
.
Глубокое копирование
Глубокое копирование создаёт новый объект и рекурсивно добавляет в него копии вложенных объектов из исходного объекта.
import copy
old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.deepcopy(old_list)
print("Old list:", old_list)
print("New list:", new_list)
2
3
4
5
6
7
Вывод программы:
Old list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
2
В этом примере не видно отличий от использования функции copy()
. Изменения видны если модифицировать вложенный список:
import copy
old_list = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
new_list = copy.deepcopy(old_list)
old_list[1][0] = 'BB'
print("Old list:", old_list)
print("New list:", new_list)
2
3
4
5
6
7
8
9
Вывод программы:
Old list: [[1, 1, 1], ['BB', 2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
2
Изменения видны только в списке old_list
, так как список new_list
содержит копии всех вложенных объектов.