Понимание dict.copy() - неглубоко или глубоко?
При чтении документации для dict.copy()
в ней говорится, что она делает мелкую копию словаря. То же самое касается книги, которую я слежу (ссылка Beazley Python Reference), в которой говорится:
Метод 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]})
Итак:
-
b = a
: Назначение ссылок, Сделать a
и b
указывает на один и тот же объект.
![Illustration of 'a = b': 'a' and 'b' both point to '{1: L}', 'L' points to '[1, 2, 3]'.]()
-
b = a.copy()
: Неверное копирование, a
и b
станут двумя изолированными объектами, но их содержимое по-прежнему имеет одну и ту же ссылку
![Illustration of 'b = a.copy()': 'a' points to '{1: L}', 'b' points to '{1: M}', 'L' and 'M' both point to '[1, 2, 3]'.]()
-
b = copy.deepcopy(a)
: глубокое копирование, структура и содержимое a
и b
становятся полностью изолированными.
![Illustration of 'b = copy.deepcopy(a)': 'a' points to '{1: L}', 'L' points to '[1, 2, 3]'; 'b' points to '{1: M}', 'M' points to a different instance of '[1, 2, 3]'.]()
Ответ 2
Это не вопрос глубокой копии или мелкой копии, ни одно из того, что вы делаете, - это глубокая копия.
Здесь:
>>> new = original
вы создаете новую ссылку на список /dict, на который ссылается оригинал.
а здесь:
>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)
вы создаете новый список /dict, который заполняется копией ссылок объектов, содержащихся в исходном контейнере.
Ответ 3
Возьмем этот пример:
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
Ответ 4
"new" и "original" - разные dicts, поэтому вы можете обновить только один из них. Элементы неточно скопированы, а не сами dict.
Ответ 5
Добавление к Кеннетму ответа. Когда вы делаете мелкую копию parent.copy(), новый словарь создается с теми же ключами, но значения не копируются, на которые они ссылаются. Если вы добавите новое значение в parent_copy не будет влиять на родительский, потому что 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]}
Значение hash (id) parent [1], parent_copy [1] идентично, что подразумевает [1,2,3] of parent [ 1] и parent_copy [1], хранящиеся при значении 140690938288400.
Но хэш родительский и parent_copy различен, что подразумевает
Это разные словари и parent_copy - это новый словарь со значениями, ссылающимися на значения parent
Ответ 6
Содержимое неточно скопировано.
Итак, если исходный dict
содержит list
или другой dictionary
, то их изменение в оригинале или его мелкой копии изменит их (list
или dict
) в другом.