理解dict.copy() – 浅或深?

在阅读dict.copy()的文档时,它说它制作了字典的浅表副本。 (Beazley的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()不能像我期望的那样工作? 或者我对浅浅复制的理解是有缺陷的?

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

 >>> 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”的示例:“a”和“b”都指向“{1:L}”,“L”指向“[1,2,3]”。

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

    “b = a.copy()”的示例:a指向“{1:L}”,“b”指向“{1:M}”,“L”和“M”都指向“ 1,2,3]'。

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

    “b = copy.deepcopy(a)”的示例:“a”指向“{1:L}”,“L”指向“[1,2,3]”; “b”指向“{1:M}”,“M”指向“[1,2,3]”的不同实例。

这不是一个深拷贝或浅拷贝的问题,你所做的一切都不是深拷贝。

这里:

 >>> new = original 

您正在创build对原始引用的列表/词典的新引用。

而这里:

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

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

以这个例子:

 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 

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

增加kennytm的答案。 当你做一个浅的副本parent.copy()时,一个新的字典是用相同的键创build的。 值不会复制它们被引用。如果您将一个新值添加到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]} 

parent [1]parent_copy [1]的哈希(id)值是相同的,这意味着存储在id为140690938288400的parent [1]parent_copy [1]的 [1,2,3]。

但是parentparent_copy的散列是不同的,这意味着它们是不同的字典,而parent_copy是一个新的字典,它具有值引用parent

内容被浅拷贝。

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