为什么dict2中嵌套字典的更改会影响dict1?

时间:2018-05-21 09:21:49

标签: python dictionary nested mutability

我不理解这些情况:

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也更改。我不明白为什么......任何想法?

3 个答案:

答案 0 :(得分:7)

请参阅shallow vs deep副本。

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

  
      
  • 浅拷贝构造一个新的复合对象,然后(尽可能)将对它的引用插入到对象中   发现在原文中。
  •   
  • 深层复制构造一个新的复合对象,然后递归地将复制品插入其中的对象中   原始
  •   

答案 1 :(得分:2)

两个代码段之间的主要区别在于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

这里要注意的关键点是

  1. k1_a = a没有制作此人的副本;类似于d1['k1'].update(content)没有复制{'v': 1}字典的方式。
  2. a.name = 'Aaron' 修改人物;类似于content['a']['v'] = 3修改内部字典的方式。
  3. 相当于你的第二个片段如下:

    a = 'Andy'
    b = 'Belle'
    
    k1_a = a
    
    a = 'Aaron'
    
    print(k1_a)  # output: Andy
    

    这一次,没有任何对象被修改。我们正在做的就是覆盖a变量的值,确切地说content['a'] = 3覆盖了你的dict中a键的值。

    如果您不希望内部词组中的更改反映在其他词组中,则必须使用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}}}
    

答案 2 :(得分:0)

如果我们用简单的作业替换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值是不可变的,因此它们的重新分配不会影响原始值。