Вопрос-Ответ

Why updating "shallow" copy dictionary doesn't update "original" dictionary? [duplicate]

Почему обновление словаря "мелкой" копии не обновляет "оригинальный" словарь?

При чтении документации для dict.copy() там говорится, что создается мелкая копия словаря. То же самое касается книги, на которую я ссылаюсь (справочник Бизли по Python), в которой говорится:


Метод m.copy() создает мелкую копию элементов, содержащихся в объекте сопоставления, и помещает их в новый объект сопоставления.


Учтите это:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

Итак, я предположил, что это обновит значение original (и добавит 'c': 3) также, поскольку я делал мелкую копию. Нравится, если вы делаете это для списка:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

Это работает так, как ожидалось.

Поскольку оба являются мелкими копиями, почему dict.copy() работает не так, как я ожидаю? Или мое понимание мелкого и глубокого копирования ошибочно?

Переведено автоматически
Ответ 1

Под "мелким копированием" подразумевается, что содержимое словаря не копируется по значению, а просто создается новая ссылка.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

Напротив, глубокая копия скопирует все содержимое по значению.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

Итак:


  1. b = a: Назначение ссылок, Make a и b указывают на один и тот же объект.


    Иллюстрация 'a = b': 'a' и 'b' оба указывают на '{1: L}', 'L' указывает на '[1, 2, 3]'.


  2. b = a.copy(): Мелкое копирование, a и b станут двумя изолированными объектами, но их содержимое по-прежнему будет иметь одну и ту же ссылку


    Иллюстрация 'b = a.copy()': 'a' указывает на '{1: L}', 'b' указывает на '{1: M}', 'L' и 'M' оба указывают на '[1, 2, 3]'.


  3. b = copy.deepcopy(a): Глубокое копирование, a и b структура и содержимое становятся полностью изолированными.


    Иллюстрация 'b = copy.deepcopy (a)': 'a' указывает на '{1: L}', 'L' указывает на '[1, 2, 3]'; 'b' указывает на '{1: M}', 'M' указывает на другой экземпляр '[1, 2, 3]'.


Ответ 2

Возьмем этот пример:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

Теперь давайте изменим значение на "мелком" (первом) уровне:

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

Теперь давайте изменим значение на один уровень глубже:

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed
Ответ 3

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

Здесь:

>>> new = original 

вы создаете новую ссылку на список / dict, на который ссылается original.

находясь здесь:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

вы создаете новый список / dict, который заполняется копией ссылок на объекты, содержащиеся в исходном контейнере.

Ответ 4

Добавление к ответу kennytm. Когда вы выполняете мелкую копию parent.copy() создается новый словарь с теми же ключами, но значения не копируются, на них ссылаются.Если вы добавите новое значение в parent_copy, это не повлияет на parent, потому что parent_copy - это новый словарь, а не ссылка.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

Значение хэша (id) для parent[1], parent_copy[1] идентичны, что подразумевает [1,2,3] для parent[1] и parent_copy[1] хранятся с идентификатором 140690938288400.

Но хэш parent и parent_copy разные, что подразумевает, что это разные словари, а parent_copy - это новый словарь, значения которого ссылаются на значения parent

python dictionary