Копирование объектов, модуль copy

В Python изменяемые объекты нельзя скопировать, присвоив одну переменной другой, так как в этом случае копируется ссылка на объект, а не он сам. В итоге при изменении объекта через одну переменную, изменения видны через другую. Поэтому используются иные способы копирования, если оно действительно необходимо.

У списков, словарей и некоторых других встроенных типов есть метод copy(), создающий их поверхностную копию. В случае поверхностной копии, если объект является составным, то есть включает другие изменяемые объекты, то они не копируются, а копируются только ссылки на них.

Если же требуется полная копия объекта, следует воспользоваться функцией deepcopy() модуля copy. Кроме этой функции там также есть функция copy(), выполняющая поверхностное копирование, аналогичное методам copy() словарей и списков.

Разницу между copy() и deepcopy() иллюстрирует пример:

>>> import copy
>>> nums = [1, 2, 3]
>>> data = {'a': 10, 'b': nums}
>>> data
{'a': 10, 'b': [1, 2, 3]}
>>> data_copy = copy.copy(data)
>>> data_deep = copy.deepcopy(data)
>>> data_copy
{'a': 10, 'b': [1, 2, 3]}
>>> data_deep
{'a': 10, 'b': [1, 2, 3]}
>>> data_copy['a'] += 2
>>> nums[1:1] = [254]
>>> data
{'a': 10, 'b': [1, 254, 2, 3]}
>>> data_copy
{'a': 12, 'b': [1, 254, 2, 3]}
>>> data_deep
{'a': 10, 'b': [1, 2, 3]}

В случае с deepcopy() была создана копия вложенного списка, copy() этого не делает.

Отсутствие переменной у списка вовсе не изменяет ситуацию:

>>> d = {1: [1,2], 2: 10}
>>> c = copy.copy(d)
>>> c
{1: [1, 2], 2: 10}
>>> c[1].append(3)
>>> c
{1: [1, 2, 3], 2: 10}
>>> d
{1: [1, 2, 3], 2: 10}

Оператор is проверяет проверяет ссылаются ли две переменные на один объект, оператор == проверяет равенство значений.

>>> d = {1:2}
>>> 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
<__main__.A object at 0x7f9b5e2bda58>
<__main__.A object at 0x7f9b5e2caac8>

Как мы видим, несмотря на то, что объекты a и b разные, поле lst обоих ссылается на один и тот же список. Чтобы при копировании объекта список был также скопирован, следует использовать функцию deepcopy().