Копирование объектов, модуль 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()
.