Глубокое и поверхностное копирование объектов в Python
В этом руководстве мы рассмотрим поверхностное и глубокое копирование с помощью Python. Обычно мы используем =(оператор присваивания) для создания копии объекта Python. Давайте разберемся с полной концепцией, связанной с созданием копий в Python.
Копирование в Python
Как мы все знаем, оператор присваивания используется для создания копии объекта Python, но это неверно; он только создает привязку между целью и объектом. Когда мы используем оператор присваивания, вместо создания нового объекта он создает новую переменную, которая использует ссылку на старый объект.
Копии нужны, когда пользователь хочет внести изменения, не изменяя при этом исходный объект. Пользователь также предпочитает создавать копию для работы с изменяемыми объектами.
Давайте разберемся в следующем примере.
list1 = [[1, 2, 3], [4, 5, 6], [7, 8, 'a']] list2 = list1 list2[1][2] = 4 print('Old List:', list1) print('ID of Old List:', id(list1)) print('New List:', list2) print('ID of New List:', id(list2))
Old List: [[1, 2, 3], [4, 5, 4], [7, 8, 'a']] ID of Old List: 1909447368968 New List: [[1, 2, 3], [4, 5, 4], [7, 8, 'a']] ID of New List: 1909447368968
В приведенном выше выводе мы видим, что обе переменные list1 и list2 имеют один и тот же идентификатор 1909447368968.
Если мы внесем какие-либо изменения в любое значение в list1 или list2, эти изменения отразятся на обоих.
Типы копий в Python
Основной мотив – создать копию объекта Python, которую мы можем изменить, не изменяя исходные данные. В Python есть два метода создания копий.
Мы будем использовать модуль копирования для создания вышеуказанных копий.
Модуль копирования
Модуль copy используется для создания мелкой и глубокой копии. Давайте посмотрим на каждый из его методов.
Мелкая копия
Неглубокая копия – это копия объекта, в которой хранятся ссылки на исходные элементы. Он создает новый объект коллекции и затем занимает его со ссылкой на дочерние объекты, найденные в оригинале.
Делает копии ссылки на вложенные объекты и не создает копии вложенных объектов. Поэтому, если мы внесем какие-либо изменения в копию объекта, это отразится на исходном объекте. Мы будем использовать функцию copy() для его реализации.
# importing "copy" for copy operations import copy # initializing list 1 list1 = [1, 7, [3,5], 8] # using copy to shallow copy list2 = copy.copy(list1) # original elements of list print("The original elements before shallow copying") for i in range(0,len(list1)): print(list1[i],end=" ") print("\r") # adding and element to new list list2[2][0] = 10 # checking if change is reflected print("The original elements after shallow copying") for i in range(0,len( list1)): print(list1[i],end=" ")
The original elements before shallow copying 1 7 [3, 5] 8 The original elements after shallow copying 1 7 [10, 5] 8
В приведенном выше коде мы сделали изменение в list1, который отражен в другом списке.
import copy list1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] list2 = copy.copy(list1) list1.append([13, 14,15]) print("Old list:", list1) print("New list:", list2)
Old list: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]] New list: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
В приведенном выше коде мы создали неглубокую копию list1. Вновь созданный список 2 содержит ссылку на исходный вложенный объект, хранящийся в списке 1. Затем мы добавили [13, 14, 15] в старый список и подсписок, не скопированный в новый список.
Глубокая копия на Python
Глубокая копия – это процесс, в котором мы создаем новый объект и рекурсивно добавляем элементы копии. Мы будем использовать метод deecopy(), который присутствует в модуле копирования. Независимая копия создается из исходного цельного объекта. Давайте разберемся в следующем примере.
import copy x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] z = copy.deepcopy(xs) print(x) prin(z)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
В приведенном выше выводе мы видим, что z является клоном x, который мы создали с помощью метода deecopy(). Если мы внесем изменения в один из дочерних объектов, это не повлияет на исходный объект.
Оба объекта полностью независимы в глубокой копии. Список x был клонирован рекурсивно, включая все его дочерние объекты.
import copy x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] z = copy.deepcopy(x) x[2][2] = 'Hello' print(x)
import copy list1 = [0, [1, 2], [3,5], 4] # using deepcopy to deep copy list2 = copy.deepcopy(list1) # original elements of list print("The original list: ") for i in range(0,len(list1)): print(list1[i],end=" ") print("\r") # adding and element to new list list2[1][0] = 8 # Change is reflected in l2 print("The new list after deep copying: ") for i in range(0,len( list1)): print(list2[i],end=" ") print("\r") # Change is NOT reflected in original list # as it is a deep copy print("The original elements:") for i in range(0,len( list1)): print(list1[i],end=" ")
The original list: 0 [1, 2] [3, 5] 4 The new list after deep copying: 0 [8, 2] [3, 5] 4 The original elements: 0 [1, 2] [3, 5] 4
Копирование произвольных объектов Python
Мы также можем скопировать произвольные объекты Python, включая пользовательские классы, с помощью метода копирования. Методы copy.copy() и copy.deepcopy() могут использоваться для дублирования любых объектов.
Давайте разберемся в следующем примере.
import copy class Func_New: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return 'Func_new(%r, %r)' %(self.x, self.y) a = Func_New(50, 56) b = copy.copy(a) print(a) print(b) print(a is b) print(b is a)
Func_new(50, 56) Func_new(50, 56) False False
В приведенном выше коде мы создали класс определения пользователя с именем Func_new и определили __repr __() для проверки объектов. Затем мы создали мелкую копию с помощью модуля копирования. Мы создали экземпляр класса и сверили оригинал и его неглубокую копию.
Что следует помнить
Составные объекты – главное отличие мелкой копии от глубокой. Объекты, которые содержат другие объекты, такие как список или экземпляр класса, называются экземплярами списка или класса. Мы можем копировать произвольные объекты(включая пользовательские классы) с помощью модуля копирования.
Модуль copy — поверхностное и глубокое копирование объектов
Операция присваивания не копирует объект, он лишь создаёт ссылку на объект. Для изменяемых коллекций, или для коллекций, содержащих изменяемые элементы, часто необходима такая копия, чтобы её можно было изменить, не изменяя оригинал. Данный модуль предоставляет общие (поверхностная и глубокая) операции копирования.
copy.copy(x) — возвращает поверхностную копию x.
copy.deepcopy(x) — возвращает полную копию x.
Исключениеcopy.error — возникает, если объект невозможно скопировать.
Разница между поверхностным и глубоким копированием существенна только для составных объектов, содержащих изменяемые объекты (например, список списков, или словарь, в качестве значений которого — списки или словари):
- Поверхностная копия создает новый составной объект, и затем (по мере возможности) вставляет в него ссылки на объекты, находящиеся в оригинале.
- Глубокая копия создает новый составной объект, и затем рекурсивно вставляет в него копии объектов, находящихся в оригинале.
Для операции глубокого копирования часто возникают две проблемы, которых нет у операции поверхностного копирования:
- Рекурсивные объекты (составные объекты, которые явно или неявно содержат ссылки на себя) могут стать причиной рекурсивного цикла;
- Поскольку глубокая копия копирует всё, она может скопировать слишком много, например, административные структуры данных, которые должны быть разделяемы даже между копиями.
Функция deepcopy решает эти проблемы путем:
- Хранения «memo» словаря объектов, скопированных во время текущего прохода копирования;
- Позволения классам, определенным пользователем, переопределять операцию копирования или набор копируемых компонентов.
Этот модуль не копирует типы вроде модулей, классов, функций, методов, следа в стеке, стековых кадров, файлов, сокетов, окон, и подобных типов.
Поверхностная копия изменяемых объектов также может быть создана методом .copy() у списков (начиная с Python 3.3), присваиванием среза (copied_list = original_list[:]), методом .copy() словарей и множеств. Создавать копию неизменяемых объектов (таких, как, например, строк) необязательно (они же неизменяемые).
Для того, чтобы определить собственную реализацию копирования, класс может определить специальные методы __copy__() и __deepcopy__(). Первый вызывается для реализации операции поверхностного копирования; дополнительных аргументов не передается. Второй вызывается для реализации операции глубокого копирования; ему передается один аргумент, словарь memo. Если реализация __deepcopy__() нуждается в создании глубокой копии компонента, то он должен вызвать функцию deepcopy() с компонентом в качестве первого аргумента и словарем memo в качестве второго аргумента.
Для вставки кода на Python в комментарий заключайте его в теги