2014-11-16 3 views
0

Я пытаюсь выполнить скрипт python, который должен сообщать разницу между мелкой и глубокой копией.Python Неглубокие и глубокие копии

Из моего понимания:

  • Мелкие копии: Создает объект и ссылки на нее, чтобы содержание было в первоначальном
  • Deep копия: Создает объект и копирует содержимое найдено в оригинале на объект

Ниже моя программа:

a = [1,2,3] 

print(id(a), id(a[0]), id(a[1])) 

print("Lets check shallow copy first") 

a1 = copy.copy(a) 
print(id(a1), id(a1[0]), id(a1[1])) 
a2 = copy.deepcopy(a) 
print(id(a2), id(a2[0]), id(a2[1])) 

Выход:

[email protected]:~/python-learning$ ./deepshallow.py 
(139854551376528, 31777112, 31777088) 
Lets check shallow copy first 
(139854551485040, 31777112, 31777088) 
(139854551378616, 31777112, 31777088) 
+0

Возможный дубликат [Почему есть нет разницы между мелкой копией и глубокой копией для списка непререкаемых] (https://stackoverflow.com/questions/23730279/why-there-is-no-difference-between-shallow-copy-and-deep-copy-for -a-list-of-immu) – chb

ответ

1

Что вы видите здесь, так это то, что целые числа одинаковы между копиями и даже глубокими копиями. Это связано с тем, что целые числа являются «неизменными», а их идентификаторы основаны на их значениях и настройках хэширования интерпретатора Python.

Попробуйте

a2[0] = 24 
assert a2[0] != a1[0] 
assert a2[0] != a[0] 
a[0] = 72 
assert a[0] != a1[0] 

Вы увидите, что даже мелкая копия не влияет. Это связано с тем, что все записи в списке неизменяемы. Для более подходящего примера попробуйте вставить списки или словари внутри друг друга и выполнить те же тесты на них.

+1

Это не совсем правильно. Например, если вы попробуете 'x = 1000; y = 1000; print id (x); print id (y); 'вы увидите, что' x' и 'y' имеют разные значения id, даже если они относятся к одному единственному неизменяемому объекту' 1000', но если вы попробуете это с помощью 'x = 100; y = 100' (по крайней мере, в CPython), вы увидите, что они имеют * то же значение '' id'. Я думаю, что путаница в том, почему элемент результата 'copy' может иметь один и тот же' id' как что-то еще, даже если дело «deepcopy», а ответ - это просто причуда CPython, что некоторые целые кешируются во время выполнения. – ely

+0

Вы правы, я сочетал 'id' с' hash' и немного ручного тестирования. – mobiusklein

1

В этом конкретном случае, поскольку элементы ваших списков являются малыми целыми числами, Python имеет встроенный механизм для ссылки на «тот же» объект (целое число), не допуская изменения в глубоко скопированном списке, чтобы вызвать изменение в списке он был скопирован.

Вот пример с целыми числами, как ваша:

In [135]: import copy 

In [136]: a1 = [1, 2, 3] 

In [137]: a2 = copy.copy(a1) 

In [138]: a3 = copy.deepcopy(a1) 

In [139]: map(id, a1) 
Out[139]: [26960216, 26960192, 26960168] 

In [140]: map(id, a2) 
Out[140]: [26960216, 26960192, 26960168] 

In [141]: map(id, a3) 
Out[141]: [26960216, 26960192, 26960168] 

Таким образом, на данный момент мы можем видеть, что списки содержат целые числа, все с тем же идентификатором. Давайте изменим элемент в глубокой копии.

In [142]: a3[0] = 1000 

In [143]: map(id, a1) 
Out[143]: [26960216, 26960192, 26960168] 

In [144]: map(id, a2) 
Out[144]: [26960216, 26960192, 26960168] 

In [145]: map(id, a3) 
Out[145]: [39759800, 26960192, 26960168] 

Теперь a3 имеет новый идентификатор для первой записи, в то время как другие списки остаются неизменными. Теперь давайте изменим первую запись мелкой копии.

In [146]: a2[0] = 1000 

In [147]: map(id, a1) 
Out[147]: [26960216, 26960192, 26960168] 

In [148]: map(id, a2) 
Out[148]: [39759200, 26960192, 26960168] 

In [149]: map(id, a3) 
Out[149]: [39759800, 26960192, 26960168] 

Обратите внимание, как на целое число 1000, которое служит в качестве первого входа для обоих a2 и a3, существует другое значение ID.

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

Вот a source describing it:

Текущая реализация хранит массив целых объектов для всех целых чисел от -5 до 256, при создании Int в этом диапазоне вы на самом деле просто получить назад ссылку на существующий объект , Поэтому должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено.:-)

Чтобы увидеть пример, где deepcopy и copy иметь значительно различное поведение, нам нужно что-то, где deepcopy «s рекурсивные вызовы к copy будет иметь значение - и это не будет для кэшируются небольшой целые числа.

Попытаемся со списком списков, и мы будем изменять содержимое, а не резко изменить то, что один из топ-большинство элементов списка относится к:

In [171]: a1 = [[1], [2], [3]] 

In [172]: a2 = copy.copy(a1); a3 = copy.deepcopy(a1) 

In [173]: a1 
Out[173]: [[1], [2], [3]] 

In [174]: a2 
Out[174]: [[1], [2], [3]] 

In [175]: map(id, a1) 
Out[175]: [140608561277264, 140608561418040, 140608561277120] 

In [176]: map(id, a2) 
Out[176]: [140608561277264, 140608561418040, 140608561277120] 

In [177]: a2[0][0] = 1000 

In [178]: a1 
Out[178]: [[1000], [2], [3]] 

In [179]: a2 
Out[179]: [[1000], [2], [3]] 

In [180]: a3 
Out[180]: [[1], [2], [3]] 

In [181]: a3[1][0] = 1001 

In [182]: a1 
Out[182]: [[1000], [2], [3]] 

In [183]: a2 
Out[183]: [[1000], [2], [3]] 

In [184]: a3 
Out[184]: [[1], [1001], [3]]