为啥更改 dict2 内的嵌套 dict 会影响 dict1? [复制]

Posted

技术标签:

【中文标题】为啥更改 dict2 内的嵌套 dict 会影响 dict1? [复制]【英文标题】:Why do changes to a nested dict inside dict2 affect dict1? [duplicate]为什么更改 dict2 内的嵌套 dict 会影响 dict1? [复制] 【发布时间】:2018-10-30 22:03:29 【问题描述】:

我不明白这些情况:

content = 'a': 'v': 1, 'b': 'v': 2
d1 = 'k1': 
d2 = 'k2': 
d1['k1'].update(content)
print(d1)
content['a']['v'] = 3
content['b']['v'] = 4
d2['k2'].update(content)
print(d2)
print(d1)
>>> 'k1': 'a': 'v': 1, 'b': 'v': 2
>>> 'k2': 'a': 'v': 3, 'b': 'v': 4
>>> 'k1': 'a': 'v': 3, 'b': 'v': 4

在上述情况下,在变量 content 更新后,d1 的内容发生了变化。

content = 'a': 1, 'b': 2
d1 = 'k1': 
d2 = 'k2': 
d1['k1'].update(content)
print(d1)
content['a'] = 3
content['b'] = 4
d2['k2'].update(content)
print(d2)
print(d1)
>>> 'k1': 'a': 1, 'b': 2
>>> 'k2': 'a': 3, 'b': 4
>>> 'k1': 'a': 1, 'b': 2 

但是在这种情况下,即使变量 content 已更改,d1 也不会更改。我不明白为什么……有什么想法吗?

【问题讨论】:

我认为所有这些赞成票但没有 cmets/answers 是因为人们也认为这是一种奇怪的行为。 :P @MateenUlhaq 不是,这与dict 是可变对象而int 是不可变的事实有关。这使得 python 以不同的方式处理它们。如果内容是list(可变),也会发生类似的事情。 第二个例子不是也改变了字典(content)吗? @MateenUlhaq 不,在第一个示例中,键指向 dict,在第二个示例中,它们指向 int。令人困惑的是这些字典的嵌套性质,但在后台,所有内容都由一堆一旦调用就会解析的指针组成。 @MateenUlhaq 如果我善于解释这一点,我会发布答案:) 【参考方案1】:

见shallow vs deep复制。

这里的副本是浅副本,因此第一级条目是副本,但嵌套结构是引用。

浅拷贝构造一个新的复合对象,然后(在可能的范围内)向其中插入对对象的引用 在原文中找到。 深拷贝构造一个新的复合对象,然后递归地将在中找到的对象的副本插入其中 原创。

【讨论】:

请不要重复回答。 我投票决定关闭它的价值。【参考方案2】:

如果我们用一个简单的赋值替换update()

# d1['k1'].update(content)
d1['k1'] = content

我们得到:

'k1': 'a': 1, 'b': 2
'k2': 'a': 3, 'b': 4
'k1': 'a': 3, 'b': 4

(这与 update 在您的示例中所做的不同。)这是因为 update 接受一个可迭代对象(例如字典)并复制键值对内部。相当于做:

d1['k1'] = k: v for k, v in content.items()

当然,int 值是不可变的,因此它们的重新分配不会影响原始值。

【讨论】:

【参考方案3】:

您的两个 sn-ps 之间的主要区别在于 content['a']['v'] = 3content['a'] = 3 是完全不同的操作。在第一种情况下,您通过更改其v修改内部字典。在后一种情况下,您替换字典中的值而不修改它。

当一切都是字典时,这很令人困惑,所以让我们将字典替换为变量和类的实例:

class Person:
    def __init__(self, name):
        self.name = name

# these two variables are represent your `content` dict
a = Person('Andy')  # this variable represents `'v': 1`
b = Person('Belle')  # this variable represents `'v': 2`

# the equivalent of `d1['k1'].update(content)` is a simple assignment
k1_a = a

# and the equivalent of `content['a']['v'] = 3` is changing a's name
a.name = 'Aaron'

# because k1_a and a are the same Person instance, this is reflected in k1_a:
print(k1_a.name)  # output: Aaron

这里要注意的关键点是

    k1_a = a 不会复制 Person;类似于d1['k1'].update(content) 不会复制'v': 1 dict。 a.name = 'Aaron' 修改 Person;类似于content['a']['v'] = 3 修改内部字典的方式。

相当于您的第二个 sn-p 如下所示:

a = 'Andy'
b = 'Belle'

k1_a = a

a = 'Aaron'

print(k1_a)  # output: Andy

这一次,没有对象被修改。我们所做的只是覆盖a 变量的值,这正是content['a'] = 3 覆盖您的dict 中a 键的值的方式。


如果您不希望内部 dicts 中的更改反映在其他 dicts 中,则必须使用 copy.deepcopy 复制它们:

import copy

content = 'a': 'v': 1, 'b': 'v': 2
d1 = 'k1': 
d2 = 'k2': 
d1['k1'].update(copy.deepcopy(content))
print(d1)
content['a']['v'] = 3
content['b']['v'] = 4
d2['k2'].update(copy.deepcopy(content))
print(d2)
print(d1)

# output:
# 'k1': 'a': 'v': 1, 'b': 'v': 2
# 'k2': 'a': 'v': 3, 'b': 'v': 4
# 'k1': 'a': 'v': 1, 'b': 'v': 2

【讨论】:

以上是关于为啥更改 dict2 内的嵌套 dict 会影响 dict1? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

检查嵌套字典值?

python 验证两个dicts dict1和dict2的名称。它会验证名称是否与小写字母不匹配。它删除了“Press Ente

dict 字典合并

[python]Python 字典(Dictionary) update()方法

python合并字典

为啥当设备在 android 中水平时,ListView 内的嵌套自定义视图会阻止正确聚焦?