Понимание dict.copy () – неглубоко или глубоко?

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

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

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

 >>> 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 : Назначение ссылок, Сделать 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]'.

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

Вот:

 >>> new = original 

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

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

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

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

Возьмем следующий пример:

 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 

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

Добавляем к ответ Кеннетму. Когда вы делаете мелкую копию 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] родительских [1] и parent_copy [1], хранящихся в id 140690938288400.

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

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

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