ChatGPT解决这个技术问题 Extra ChatGPT

理解 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() 不能像我预期的那样工作?还是我对浅拷贝和深拷贝的理解有缺陷?

奇怪的是他们没有解释“浅”。内幕知识,眨眼。只有字典和键是副本,而第一级内的嵌套字典是引用,例如不能在循环中删除。因此,在这种情况下,Python 的 dict.copy() 既无用也不直观。谢谢你的问题。

k
kennytm

“浅拷贝”意味着字典的内容不是按值拷贝的,而只是创建一个新的引用。

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

所以:

b = a:引用赋值,使a和b指向同一个对象。 b = a.copy():浅拷贝,a和b会成为两个孤立的对象,但它们的内容仍然共享相同的引用 b = copy.deepcopy(a):深拷贝,a和b的结构和内容变得完全孤立。


不错的答案,但您可能会考虑纠正第一句话中的语法错误。并且没有理由不在 b 中再次使用 L。这样做会简化示例。
@kennytm:实际上,前两个示例有什么区别?你得到了相同的结果,但内部实现略有不同,但这有什么关系呢?
@JavaSa a[1] = {} 将影响第一个示例中的 b,而不会影响第二个示例中的 b
很好的解释,......真的拯救了我的一天!谢谢......这同样可以应用于python的列表,str和其他数据类型吗?
当您 dic_b = dic_a 时,@Sheldore dic_adic_b 正在引用同一个字典。字典中的字符串是不可变的,但这无关紧要,因为 dic_b['A'] = 'Adam' 会改变字典本身。
e
eumiro

举个例子:

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

no change in original, since ['a'] is an immutable integer 这个。它实际上回答了所提出的问题。
L
Lie Ryan

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

这里:

>>> new = original 

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

在这里:

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

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


是的,就目前而言......但它还不足以回答关于浅层与深层的 OP 问题。
V
Vkreddy

添加到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]}

parent[1] 和 parent_copy[1] 的 hash(id) 值相同,这意味着 parent[1] 和 parent_copy[1] 的 [1,2,3] 存储在 id 140690938288400 中。

但是 parent 和 parent_copy 的哈希值不同,这意味着它们是不同的字典,而 parent_copy 是一个新字典,其值引用了 parent 的值


J
Joril

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


J
Jungle Hunter

内容是浅拷贝。

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


S
Stephen Rauch

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

.copy= 是不同的东西。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅