Копирование объектов¶
Операция присваивания не копирует объект, а лишь создает ссылку на объект. Если нам нужно скопировать объект, то стоит сделать его копию - поверхностную или глубокую.
Поверхностная копия¶
Поверхностная копия - это такая копия, при которой создается новый объект и затем в новый объект вставляются ссылки на объекты, которые были у копируемого объекта.
Поверхностную копию можно сделать при помощи copy.copy()
.
Например:
import copy
class Test:
pass
first = [Test()]
print("# first")
print(f"Container ID={id(first)}, el={id(first[0])}")
# Container ID=140323889213760, el=140323888984464
copied = copy.copy(first)
print("# copied")
print(f"Container ID={id(copied)}, el={id(copied[0])}")
# Container ID=140323889217152, el=140323888984464
Как видим, идентификаторы контейнеров у нас разные - при копировании создался новый список. А вот объект внутри списка новый не создался - ссылка в copied
перешла ссылка на него из first
.
Переопределить поведение функции можно при помощи магического метода __copy__
.
Копирование иммутабельных объектов¶
Если мы попробуем скопировать иммутабельный объект, то получим ссылку на него. Пример с кортежом:
Глубокая копия¶
Глубокая копия создает новый объект, а затем рекурсивно пытается сделать копии внутренних объектов. Например:
first = [Test()]
print("# first")
print(f"Container ID={id(first)}, el={id(first[0])}")
# Container ID=139994093187456, el=139994092958096
copied = copy.deepcopy(first)
print("# copied")
print(f"Container ID={id(copied)}, el={id(copied[0])}")
# Container ID=139994093190784, el=139994093209920
Индентификатор контейнера и идентификатор объекта в контейнере разные.
Проблемы при создании глубокой копии¶
- Рекурсивные объекты (которые содержат ссылки сами на себя) могут стать причиной рекурсивного цикла.
- Не всё можно скопировать.
- Иногда могут копироваться данные, которые должны быть разделяемы между копиями
deepcopy
решает эти проблемы следующим способом:
- Поведение при применении функции
deepcopy
можно поменять при помощи магического метода__deepcopy__
. Таким образом при копировании можно не копировать некоторые объекты, а оставлять ссылку на них. deepcopy
может вторым аргументом принимать словарьmemo
, который хранит уже скопированные объекты во время текущего прохода копирования. Это помогает при копировании рекурсивных объектов.