了解dict.copy() - 浅或深?

use*_*312 402 python

在阅读文档时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}
Run Code Online (Sandbox Code Playgroud)

所以我假设这会更新original(并添加'c':3)的值,因为我正在做一个浅拷贝.就像你为列表做的那样:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])
Run Code Online (Sandbox Code Playgroud)

这按预期工作.

由于两者都是浅拷贝,为什么dict.copy()它不能像我期望的那样工作?或者我对浅层和深层复制的理解是有缺陷的?

ken*_*ytm 950

通过"浅层复制",它意味着字典的内容不会被值复制,而只是创建一个新的引用.

>>> 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]})
Run Code Online (Sandbox Code Playgroud)

相反,深拷贝将按值复制所有内容.

>>> 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]})
Run Code Online (Sandbox Code Playgroud)

所以:

  1. b = a:引用赋值,Make ab指向同一个对象.

    '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):深复制,ab的结构和内容变得完全隔离.

    'b = copy.deepcopy(a)'的插图:'a'指向'{1:L}','L'指向'[1,2,3]';  'b'指向'{1:M}','M'指向'[1,2,3]'的不同实例.

  • 很棒的解释,......真的救了我的一天!谢谢......这可以应用于python的list,str和其他数据类型吗? (2认同)

Lie*_*yan 37

这不是深拷贝或浅拷贝的问题,你所做的都不是深拷贝.

这里:

>>> new = original 
Run Code Online (Sandbox Code Playgroud)

您正在创建对原始引用的列表/字典的新引用.

在这里:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)
Run Code Online (Sandbox Code Playgroud)

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


eum*_*iro 27

举个例子:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()
Run Code Online (Sandbox Code Playgroud)

现在让我们改变'浅'(第一)级别的值:

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
Run Code Online (Sandbox Code Playgroud)

现在让我们将值更深一级:

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
Run Code Online (Sandbox Code Playgroud)

  • `原来没有变化,因为['a']是一个不可变的整数.这个.它实际上回答了问题. (5认同)

Vkr*_*ddy 7

添加到kennytm的答案.当您执行浅拷贝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]}
Run Code Online (Sandbox Code Playgroud)

parent [1]的hash(id)值,parent_copy [1]是相同的,它表示存储在id 140690938288400 的parent [1]parent_copy [1]的 [1,2,3] .

但是parentparent_copy的哈希是不同的,这意味着它们是不同的词典,而parent_copy是一个新的词典,其值引用了父级的


Jor*_*ril 5

"new"和"original"是不同的dicts,这就是为什么你只能更新其中一个.这些项目是浅层复制的,而不是dict本身.