Python深度复制,对象中的字典值更改

时间:2014-10-21 04:54:01

标签: 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):
            print "BACKUP BAR IS: ", backup.bar
            self.bar[1] = "42"
            self.bar = backup.bar

a = Foo({1:"0"})
a.loop()

前两次打印出BACKUP BAR IS: {1: '1'},然后在接下来的八次打印出BACKUP BAR IS: {1: '42'}。任何人都可以告诉我这是怎么回事以及为什么会这样? deepcopy是否完全创建了self的新实例?当我们只更改“self”的值时,backup条的值如何变化,在这种情况下是“a”?

编辑:有人注意到行self.bar = backup.bar导致两个词汇指向彼此。但是,如果我们这样做:

 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}。因此,改变左侧的参考不会改变右侧的参考。同样,如果我们只更改backup.bar

,如何更改self.bar

3 个答案:

答案 0 :(得分:4)

设置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之后,指向c指向的

a ---------------
                |
                |
b ---> {1: 2}   |
c ---> {1: 3} <-|

答案 1 :(得分:2)

您的问题在于以下两行:

self.bar[1] = "42"
self.bar = backup.bar

在第一次迭代中,你确实没有改变backup.bar这就是为什么在第二次迭代开始时它再次打印{1:'1'}

但是,由于您设置了self.bar = backup.bar,然后通过更改self.bar,您实际上正在改变backup.bar,因为现在两者都指向同一个对象。因此,从现在开始(第二次迭代 - 打印之后),所有打印将显示BACKUP BAR IS: {1: '42'}(并且从第二次迭代开始 - 第二次分配将是多余的,因为同样,两点都是到同一个对象)。

答案 2 :(得分:1)

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

self.bar = backup.bar

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