Модуль 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 в комментарий заключайте его в теги
Как взять значение из одной переменной в другую, но не делать ссылку на неё?
Только для списка без вложений objects2 = objects[:] Ну а для других объектов, естественно, copy/deepcopy .
Отслеживать
ответ дан 30 сен 2022 в 20:44
Alex Titov Alex Titov
1,241 7 7 серебряных знаков 8 8 бронзовых знаков
Для получения "мелкой" копии (достаточной в вашем случае) есть три равноценных способа:
- objects2 = objects.copy()
- objects2 = list(objects)
- objects2 = objects[:]
Однако, лучше всего использовать .copy() , как наиболее понятный способ - мы делаем копию, и это видно по названию используемого метода. "Явное лучше не явного".
Для получения "глубокой" копии (когда в списке не значения, а объекты) нужно использовать метод copy.deepcopy :
from copy import deepcopy objects2 = deepcopy(objects)
В Питоне переменные передаются по ссылке или по значению? Есть подводные камни?
Неизменяемые объекты передаются по значению. Это значит, что при изменении значения переменной будет создан новый объект. К этому типу относятся:
- числовые данные (int, float, complex)
- символьные строки (str)
- кортежи (tuple) При инициализации переменной незменяемого типа создается объект (например, целое число), этот объект имеет некоторый идентификатор:
>>> a = 10 >>> id(a) 10914656
оператор = связывает переменную a и объект посредством ссылки. При этом вы не можете изменить сам объект, т.е. когда вы присвоите переменной новое значение, интерпретатор создаст новый объект (если до этого этот объект был создан, то переменная просто получит ссылку), а первоначальный объект удалится из памяти сбощиком мусора, если ссылок на него больше нет.
Изменяемые (mutable)
Изменяемые объекты передаются по ссылке. Это значит, что при изменении значения переменной объект будет изменен. К этому типу относятся:
- списки (list)
- множества (set)
- словари (dict)
Подводные камни
Создадим список a , установим для переменной b ссылку на a , прибавим к b элемент списка и выведем их значения и идентификаторы на экран:
>>> a = [1, 2] >>> b = a >>> b.append(3) >>> print(a, b) [1, 2, 3] [1, 2, 3] >>> print(id(a), id(b)) 139748057891656 139748057891656
Как мы видим, переменные имеют одинаковые id и элементы списка. Если ты не знаешь об этой особенности изменяемых объетов, то такое поведение программы для тебя становится полной неожиданностью и может привести к ошибке в работе программы. Таким же образом с помощью ссылки на изменяемый объект, переменная передается в функцию:
>>> def add_value(a): . a.append(3) >>> b = [1, 2] >>> add_value(b) >>> print(b) [1, 2, 3]
Даже возвращая None , функция изменила список b , чего бы нам не хотелось.
Что с этим можно сделать
Для того, чтобы передать в функцию изменяемую переменную как значение, нужно сделать копию изменяемого элемента. Создадим копию списка:
новый_лист = старый_лист[:]
Тоже самое можем сделать вот так:
новый_лист = list(старый_лист)
Переменная новый_лист ссылается на новый объект:
>>> id(старый_лист), id(новый_лист) (139748050279112, 139748057891656)
Это дает нам возможность изменять оба объекта независимо друг от друга.
Что почитать
- Переменные-ссылки в Python
- Understanding Python variables and Memory Management
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Копирование объектов, модуль copy
В Python изменяемые объекты нельзя скопировать, присвоив одну переменной другой, так как в этом случае копируется ссылка на объект, а не он сам. В итоге при изменении объекта через одну переменную, изменения видны через другую. Поэтому используются иные способы копирования, если оно действительно необходимо.
У списков, словарей и некоторых других встроенных типов есть метод copy() , создающий их поверхностную копию. В случае поверхностной копии, если объект является составным, то есть включает другие изменяемые объекты, то они не копируются, а копируются только ссылки на них.
Если же требуется полная копия объекта, следует воспользоваться функцией deepcopy() модуля copy . Кроме этой функции там также есть функция copy() , выполняющая поверхностное копирование, аналогичное методам copy() словарей и списков.
Разницу между copy() и deepcopy() иллюстрирует пример:
>>> import copy >>> nums = [1, 2, 3] >>> data = >>> data >>> data_copy = copy.copy(data) >>> data_deep = copy.deepcopy(data) >>> data_copy >>> data_deep >>> data_copy['a'] += 2 >>> nums[1:1] = [254] >>> data >>> data_copy >>> data_deep
В случае с deepcopy() была создана копия вложенного списка, copy() этого не делает.
Отсутствие переменной у списка вовсе не изменяет ситуацию:
>>> d = >>> c = copy.copy(d) >>> c >>> c[1].append(3) >>> c >>> d
Оператор is проверяет проверяет ссылаются ли две переменные на один объект, оператор == проверяет равенство значений.
>>> d = >>> c = d >>> e = d.copy() >>> d is c True >>> d is e False >>> d == e True
С помощью функций модуля copy можно копировать объекты собственных классов:
import copy class A: def __init__(self): self.lst = [] a = A() a.lst.append(10) b = copy.copy(a) b.lst[0] = 20 print(a.lst, b.lst) print(a is b) print(a) print(b)
[20] [20] False
Как мы видим, несмотря на то, что объекты a и b разные, поле lst обоих ссылается на один и тот же список. Чтобы при копировании объекта список был также скопирован, следует использовать функцию deepcopy() .