Python deepcopy, dictionary value in object changes
Question: I have a python code snippet here:

    
    
    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()
    

This prints out `BACKUP BAR IS: {1: '1'}` the first two times, but then it
starts to print out `BACKUP BAR IS: {1: '42'}` the next eight times. Can
anyone tell my how and why this is happening? Doesn't `deepcopy` completely
create a new instance of `self`? How does the value of `backup` bar change
when we only change the value of "self", which is "a", in this case?

Edit: some have noted that the line `self.bar = backup.bar` causes the two
dicts to point to each other. But, if we do:

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

If a and b indeed point to the same dictionary, then all three values of a, b,
c should be equal to c. But instead, `a = c = {1:3}, b = {1:2}`. So changing
the reference of the left hand side doesn't change the reference of the right
hand side. Similarly, how would `backup.bar` be changed if we're only changing
`self.bar`?


Answer: Setting `self.bar = backup.bar` does not mutate the dictionary that `self.bar`
is pointing to, nor does it create a copy of `backup.bar` to assign to
`self.bar`. Rather, it changes the pointer of `self.bar` to refer to the
dictionary at `backup.bar`, so that they now refer to the same object. Take a
look at this demonstration:

    
    
    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'}
    

The discrepancy with iteration is explained by @alfasin.

With regard to your question edit, you never change the dictionary that `b`
points to. You're simply changing where `a` is pointing to twice. `a = c` does
not mean _change b = c_. It simply means _a was pointing to b, now it's
pointing to c_.

**Edit** drawings because why not.

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

After `a = b`:

    
    
    a --------   # So long, {1: 1}
             |
             v
    b ---> {1: 2}
    c ---> {1: 3}
    

After `a = c`, _i.e., a points to what c points to_ :

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

