Python deepcopy,对象中的字典值更改

Python deepcopy,对象中的字典值更改,python,reference,copy,Python,Reference,Copy,我这里有一段python代码片段: import copy class Foo(object): bar = dict() def __init__(self, bar): self.bar = bar def loop(self): backup = copy.deepcopy(self) backup.bar[1] = "1" for i in range(0, 10): p

我这里有一段python代码片段:

import copy

class Foo(object):
    bar = dict()

    def __init__(self, bar):
        self.bar = bar

    def loop(self):
        backup = copy.deepcopy(self)
        backup.bar[1] = "1"
        for i in range(0, 10):
            print "BACKUP BAR IS: ", backup.bar
            self.bar[1] = "42"
            self.bar = backup.bar

a = Foo({1:"0"})
a.loop()
前两次打印的备份条是:{1:'1'},但随后开始打印
备份条是:{1:'42'}
接下来的八次。有人能告诉我这是怎么发生的以及为什么发生的吗?
deepcopy
是否完全创建了
self
的新实例?在这种情况下,当我们只更改“self”的值(即“a”)时,
backup
bar的值如何变化

编辑:有些人注意到,行
self.bar=backup.bar
导致两个dict相互指向。但是,如果我们这样做:

 a = {1:1}
 b = {1:2}
 c = {1:3}
 a = b
 a = c

如果a和b确实指向同一个字典,那么a、b、c的三个值都应该等于c。但是相反,
a=c={1:3},b={1:2}
。因此,更改左手侧的参考并不会更改右手侧的参考。类似地,如果我们只更改
self.bar
,如何更改
backup.bar

您的问题在于以下两行:

self.bar[1] = "42"
self.bar = backup.bar
在第一次迭代中,您确实没有更改
backup.bar
,这就是为什么在第二次迭代开始时,它会再次打印
{1:'1'}


但是,由于您设置了
self.bar=backup.bar
,然后通过更改
self.bar
,实际上您正在更改
backup.bar
,因为现在两者都指向同一对象。因此,从现在开始(第二次迭代-打印后),所有打印都将显示备份栏为:{1:'42'}(从第二次迭代开始,第二次赋值将是多余的,因为它们都指向同一个对象)。

我相信您的问题在于这一行

self.bar = backup.bar

您正在将self.bar设置为现在指向backup.bar,因此当您修改self.bar时,您也在修改backup.bar,反之亦然

设置
self.bar=backup.bar
不会变异
self.bar
指向的词典,也不会创建
backup.bar
的副本以分配给
self.bar
。相反,它将
self.bar
的指针更改为引用
backup.bar
中的字典,以便它们现在引用相同的对象。看看这个演示:

In [48]: a = Foo({1: "0"})

In [49]: backup = copy.deepcopy(a)

In [50]: a.bar = backup.bar

In [51]: id(backup.bar)
Out[51]: 140428501511816

In [52]: id(a.bar)  # Same object! Alternatively, `a.bar is backup.bar` -> True
Out[52]: 140428501511816

In [53]: backup.bar[1] = "42"

In [54]: a.bar
Out[54]: {1: '42'}
迭代的差异由@alfasin解释

关于问题编辑,您从不更改
b
指向的词典。您只需更改
a
指向两次的位置<代码>a=c并不意味着更改b=c。它只是意味着a指向b,现在它指向c

编辑图形,因为为什么不编辑

a ---> {1: 1}
b ---> {1: 2}
c ---> {1: 3}
a=b
之后:

a --------   # So long, {1: 1}
         |
         v
b ---> {1: 2}
c ---> {1: 3}
a=c
之后,即a指向c指向的对象:

a---------------
|
|
b--->{1:2}|

好的,这就是我想的,但是如果我们用普通字典试试这个:让a,b,c成为字典,设置a=b和a=c。当我在解释器中尝试这一点时,我不再得到b=c或a=b。@lu6cifer最好发布一个代码示例,因为理解注释中的“代码”有点问题。我希望您知道
bar=dict()
self.bar
是两个不同的对象(静态类成员和实例成员)在上一个示例中,您将
b
分配给
a
,然后将
c
分配给
a
,因此您最终将
a
指向
c
,但是
b
没有改变,为什么您希望它也指向
c
a ---------------
                |
                |
b ---> {1: 2}   |
c ---> {1: 3} <-|