Поверхностное и глубокое копирование в Python
Копирование объекта в Python
В Python мы используем оператор = для создания копии объекта. Вы можете подумать, что это создаёт новый объект; это не так. Это создаёт только новую переменную, которая разделяет ссылку на исходный объект.
Давайте рассмотрим пример, где мы создаём список с именем old_list и передаём ссылку на объект в new_list с помощью оператора =.
Пример 1: Копирование с использованием оператора =
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))
Когда мы запускаем приведённую выше программу, вывод будет:
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
Как видно из вывода, обе переменные old_list и new_list имеют один и тот же id, т.е. 140673303268168.
Таким образом, если вы хотите изменить любые значения в new_list или old_list, изменение будет видно в обоих.
По сути, иногда вы можете захотеть, чтобы исходные значения остались неизменными, а изменялись только новые значения, или наоборот. В Python есть два способа создания копий:
Поверхностная копия (Shallow Copy)
Глубокая копия (Deep Copy)
Чтобы эти копии работали, мы используем модуль copy.
Модуль copy
Мы используем модуль Python copy для операций поверхностного и глубокого копирования. Предположим, вам нужно скопировать составной список, скажем, x. Например:
import copy
copy.copy(x)
copy.deepcopy(x)
Здесь copy() возвращает поверхностную копию x. Аналогично, deepcopy() возвращает глубокую копию x.
Поверхностное копирование (Shallow Copy)
Поверхностная копия создаёт новый объект, который хранит ссылку на исходные элементы.
Таким образом, поверхностная копия не создаёт копию вложенных объектов, а просто копирует ссылки на вложенные объекты. Это означает, что процесс копирования не рекурсивен и не создаёт копии самих вложенных объектов.
Пример 2: Создание копии с использованием поверхностного копирования
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)
При запуске программы вывод будет:
Old list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
В приведённой выше программе мы создали вложенный список и затем выполнили его поверхностное копирование с помощью метода copy().
Это означает, что он создаст новый и независимый объект с тем же содержимым. Чтобы проверить это, мы печатаем как old_list, так и new_list.
Чтобы подтвердить, что new_list отличается от old_list, мы пытаемся добавить новый вложенный объект к оригиналу и проверить его.
Пример 3: Добавление [4, 4, 4] к old_list, используя поверхностное копирование
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)
При запуске программы будет выведено:
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]]
В приведённой выше программе мы создали поверхностную копию old_list. new_list содержит ссылки на исходные вложенные объекты, хранящиеся в old_list. Затем мы добавляем новый список, т.е. [4, 4, 4], в old_list. Этот новый подсписок не был скопирован в new_list.
Однако, когда вы изменяете любые вложенные объекты в old_list, изменения появляются в new_list.
Пример 4: Добавление нового вложенного объекта с использованием поверхностного копирования
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)
При запуске программы будет выведено:
Old list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 'AA', 2], [3, 3, 3]]
В приведённой выше программе мы внесли изменения в old_list, т.е. old_list[1][1] = 'AA'. Оба подсписка old_list и new_list по индексу [1][1] были изменены. Это потому, что оба списка разделяют ссылку на одни и те же вложенные объекты.
Глубокое копирование (Deep Copy)
Глубокая копия создаёт новый объект и рекурсивно добавляет копии вложенных объектов, присутствующих в исходных элементах.
Давайте продолжим с примера 2. Однако мы собираемся создать глубокую копию, используя функцию deepcopy(), присутствующую в модуле copy. Глубокая копия создаёт независимую копию исходного объекта и всех его вложенных объектов.
Пример 5: Копирование списка с помощью deepcopy()
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)
При запуске программы будет выведено:
Old list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
В приведённой выше программе мы используем функцию deepcopy() для создания копии, которая выглядит похожей.
Однако, если вы внесёте изменения в любые вложенные объекты в исходном объекте old_list, вы не увидите изменений в копии new_list.
Пример 6: Добавление нового вложенного объекта в список с использованием глубокого копирования
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)
При запуске программы будет выведено:
Old list: [[1, 1, 1], ['BB', 2, 2], [3, 3, 3]]
New list: [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
В приведённой выше программе, когда мы присваиваем новое значение old_list, мы видим, что изменена только old_list. Это означает, что old_list и new_list независимы. Это происходит потому, что old_list был скопирован рекурсивно, что справедливо для всех его вложенных объектов.