Поверхностное и глубокое копирование в 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))
1
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
1
2
3
4
5

Обе переменные ссылаются на один объект, это подтверждает одинаковый идентификатор объекта 140673303268168. Так что при изменении значений в переменных new_list и old_list, изменение будет видно в обоих переменных.

Иногда нужно оставить исходное значение без изменений и модифицировать только новое. В Python, есть два способа сделать копию: поверхностное копирование и глубокое копирование.

Модуль Copy

Модуль copy используется для поверхностного и глубокого копирования. Например:

import copy

copy.copy(x)
copy.deepcopy(x)
1
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)
1
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]]
1
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)
1
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]]
1
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)
1
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]]
1
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)
1
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]]
1
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)
1
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]]
1
2

Изменения видны только в списке old_list, так как список new_list содержит копии всех вложенных объектов.

Последниее изменение: 24.08.2023, 06:42:55