理解dict.copy() -浅还是深?

在阅读dict.copy()的文档时,它说它对字典做了一个浅拷贝。我正在阅读的这本书(Beazley's 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()不像我期望的那样工作?或者我对浅复制和深复制的理解是有缺陷的?

304106 次浏览

通过“浅复制”,它意味着字典的内容不按值复制,而只是创建一个新的引用。

>>> 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:引用赋值,使ab指向同一个对象。

     'a = b': '和& # 39;b # 39;{1: L}', 'L'指向'[1,2,3]'。 < / p >

  2. b = a.copy():浅复制,ab将成为两个孤立的对象,但它们的内容仍然共享相同的引用

    插图'b = a.copy()'{1: L}', 'b'{1: M}', 'L'猴和& # 39;# 39;两者都指向'[1,2,3]'。 < / p >

  3. b = copy.deepcopy(a):深度复制,ab的结构和内容完全隔离。

     'b = copy.deepcopy(a)'{1: L}', 'L'指向'[1,2,3]';& # 39; b # 39;{1: M}', '指向'[1,2,3]'的不同实例。 < / p >

“新”和“原始”是不同的字典,这就是为什么你只能更新其中一个。项目是浅复制的,而不是字典本身。

内容是浅复制。

因此,如果原始的dict包含list或另一个dictionary,在原始或其浅拷贝中修改其中一个将在另一个中修改它们(listdict)。

举个例子:

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

现在让我们在“shallow”(第一个)层中改变一个值:

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

这不是深度复制或浅复制的问题,你所做的都不是深度复制。

在这里:

>>> new = original

您正在创建一个对original引用的列表/字典的新引用。

而在这里:

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

您正在创建一个新的列表/字典,其中填充了原始容器中包含的对象引用的副本。

加上kennytm的答案。当你做一个浅拷贝< em > parent.copy () < / em >时,一个新的字典被创建了相同的键,但是值没有被复制,而是被引用。如果你添加一个新值到< em > parent_copy < / em >,它不会影响父母< em > < / em >,因为< em > parent_copy < / em >是一个新字典而不是引用。

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]}

父[1]< em > < / em >< em > parent_copy [1] < / em >的哈希(id)值是相同的,这意味着父[1]< em > < / em >< em > parent_copy [1] < / em >的[1,2,3]存储在id 140690938288400。

但是父母< em > < / em >< em > parent_copy < / em >的哈希是不同的,这意味着 它们是不同的字典,< em > parent_copy < / em >是一个新字典,它的值引用于父母< em > < / em >

的值

在第二部分中,您应该使用new = original.copy()

.copy=是不同的东西。