Понимание dict.copy () - мелкое или глубокое? - PullRequest
378 голосов
/ 20 октября 2010

При чтении документации для 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() не работает так, как я ожидаюк?Или мое понимание мелкого и глубокого копирования неверно?

Ответы [ 7 ]

908 голосов
/ 20 октября 2010

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

>>> 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 указывает на один и тот же объект.

    Illustration of 'a = b': 'a' and 'b' both point to '{1: L}', 'L' points to '[1, 2, 3]'.

  2. 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]'.

  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]'.

37 голосов
/ 20 октября 2010

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

Здесь:

>>> new = original 

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

в то время как здесь:

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

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

25 голосов
/ 20 октября 2010

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

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
7 голосов
/ 31 июля 2017

Добавление к ответу Кеннимма. Когда вы делаете мелкую копию 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

5 голосов
/ 20 октября 2010

«новый» и «оригинальный» - это разные диктанты, поэтому вы можете обновить только один из них. элементы копируются мелко, а не сам диктат.

2 голосов
/ 20 октября 2010

Содержимое мелко скопировано.

Таким образом, если оригинал dict содержит list или другой dictionary, изменение одного из них в оригинале или его мелкой копии приведет к изменениюих (list или dict) в другом.

1 голос
/ 12 апреля 2018

Во второй части вы должны использовать new = original.copy()

.copy и = - разные вещи.

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