-2

I'm having a problem using dictionaries of dictionaries, that can be described by the code below:

Dic_A = {}
Dic_B = {}

Dic_A["A1"] = "1"
Dic_A["A2"] = "2"

Dic_B["AA"] = Dic_A

print "Dic_B after : " + str(Dic_B)

Dic_A["A1"] = "4"
Dic_A["A2"] = "5"

print "Dic_B before: " + str(Dic_B)

Dic_B["AB"] = Dic_A

print "Dic_B after : " + str(Dic_B)

The output I get is:

Dic_B after : {'AA': {'A1': '1', 'A2': '2'}}

Dic_B before: {'AA': {'A1': '4', 'A2': '5'}}

Dic_B after : {'AA': {'A1': '4', 'A2': '5'}, 'AB': {'A1': '4', 'A2': '5'}}

Why is Dic_B["AA"] updated when I update Dic_A? And how do I get around this problem?

Thanks in advance :-)

Nick

sshashank124
  • 31,495
  • 9
  • 67
  • 76
  • 3
    Too many questions like this :/ – vaultah Apr 16 '14 at 15:23
  • Sorry, I couldn't find anything via the search (difficult to know what to search for really). If you could point me to a solution/explanation that would be helpful. Thanks. – Nick Fromage Apr 16 '14 at 15:25
  • If you want a dictionary of other dictionaries, then this is what you expect, no? You create dictionary A and put it in B, it seems you would want this behavior. If what you want instead is a dictionary that is just like dictionary A in dictionary B ( a copy ), then you should consult the three correct answers posted. – bsoist Apr 16 '14 at 15:46
  • What Python was doing seemed counter-intuitive to me, so did a bit of research - for others who are interested, this is a pretty good explanation: http://python.net/~mwh/hacks/objectthink.html – Nick Fromage Apr 17 '14 at 13:54

2 Answers2

3

Why is Dic_B["AA"] updated when I update Dic_A? And how do I get around this problem?

Because Dic_B["AA"] is Dic_A. That's what Dic_B["AA"] = Dic_A means. Try print(Dic_B["AA"] is Dic_A. You'll notice that it prints True, because they are literally the same object.

If you don't want to store two references to the same dictionary, you need to create new objects instead of reassigning values within the old one.

dic_a = {} # new dictionary
dic_a['A1'] = '1'
dic_a['A2'] = '2'
dic_b['AA'] = dic_a # store dic_a in dic_b

dic_a = {} # CREATE a new dictionary and point the name dic_a to it.

dic_a['A1'] = '1'
dic_a['A2'] = '2'
dic_b['AA'] = dic_a # store the NEW dic_a in dic_b

Of course, there are better ways to do this, and some things you could do to make your life easier like naming the dictionaries better. For instance, you could take advantage of the fact that the dict type can copy dictionaries into new instances, so if you want to keep reusing the source dictionary, you can do that and just always insert copies into the parent dictionary:

# put stuff in dic_a...
dic_b['AA'] = dict(dic_a) # put a copy of dic_a into dic_b

# put different stuff in dic_a...
dic_b['AB'] = dict(dic_a) # put a copy of the altered dic_a into dic_b
Henry Keiter
  • 16,863
  • 7
  • 51
  • 80
  • Thanks for the help and explanation - Much appreciated :-) A bit weird how a dictionary is copied by reference, but a variable is copied by value - I'm sure there's a good reason this!?! . – Nick Fromage Apr 17 '14 at 13:16
  • @Nick It's more consistent than you think. See [this answer](http://stackoverflow.com/a/986145/2069350) for a very good explanation. Essentially it has nothing to do with "pass-by-ref" or "pass-by-value", but rather the fact that (for instance) dictionaries are mutable objects, but strings are not. – Henry Keiter Apr 17 '14 at 13:47
2

That's what they call Principle of least astonishment. Just kidding.

Do this:

Dic_B["AA"] = Dic_A
print(Dic_B['AA'] is Dic_A)

and you'll see True in the output. That means that the object you think you've copied by value was copied by reference, and if you modify it in one place, it will also get modified in the other.

Also take a look at this question.

There're sh*tload of solutions:

from copy import copy
Dic_B["AA"] = copy(Dic_A)

or

from copy import deepcopy
Dic_B["AA"] = deepcopy(Dic_A)

or

Dic_B["AA"] = dict(Dic_A)

or

Dic_B["AA"] = {}
Dic_B["AA"].update(Dic_A) 

All of the solutions will solve your problem, however only second one will copy values from Dic_A by value (and not by reference). For example, Dic_A may contain some list object and only deepcopy will copy it by value, other solutions will copy it by reference:

>>> from copy import copy, deepcopy
>>> Dic_A = {'key': ['v', 'a', 'l', 'u', 'e']}
>>> Dic_B = copy(Dic_A) 
>>> Dic_B is Dic_A # Different `dict` objecst, but ...
False
>>> Dic_B['key'] is Dic_A['key'] # The same `list` object
True
>>> Dic_B = deepcopy(Dic_A)
>>> Dic_B is Dic_A # Different `dict` objects
False
>>> Dic_B['key'] is Dic_A['key'] # and `list` objects
False

So, I think deepcopy is better in cases when you need to copy the whole object by value.

Community
  • 1
  • 1
vaultah
  • 44,105
  • 12
  • 114
  • 143
  • Thanks for the help and explanation - much appreciated :-) I ended up using 'deepcopy', rather trying to re-write the code (it's just a one off text crunching script - as long as it works it doesn't have to e fast/pretty) A bit weird how a dictionary is copied by reference, but a variable is copied by value - I'm sure there's a good reason this!?! "Principle of least astonishment" :-) – Nick Fromage Apr 17 '14 at 13:20
  • @NickFromage added a bit more explanation – vaultah Apr 17 '14 at 14:16