'Python: nested dictionary is changing all values at once
I need to generate nested dictionaries, so I wrote the function create_nested
:
def create_nested():
key_dict = {}
for key in ['key1', 'key2']:
key_dict[key] = 0
nested_dict = {}
for dictionary in ['dict1', 'dict2']:
nested_dict[dictionary] = key_dict
return nested_dict
Which will always return something like this:
{'dict1': {'key1': 0, 'key2': 0}, 'dict2': {'key1': 0, 'key2': 0}}
When a try to change one of the values this way:
x=create_nested()
x['dict1']['key2'] = 2
It gives me {'dict1': {'key1': 0, 'key2': 2}, 'dict2': {'key1': 0, 'key2': 2}}
Instead of {'dict1': {'key1': 0, 'key2': 2}, 'dict2': {'key1': 0, 'key2': 0}}
What am I doing wrong?
Solution 1:[1]
When you assign the key_dict to multiple values in nested_dict, they will refer to the same object in memory (I hope my terminology is correct. Python is Pass-by-object-reference, full explaination here https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/ )
for dictionary in ['dict1', 'dict2']:
nested_dict[dictionary] = key_dict
return nested_dict
now if I change nested_dict['dict1'] I'm changing the same memory that nested_dict['dict2'] knows about. In general, know which types are mutable, and expect them to change if another reference to it changes it.
A simple fix for dictionaries that only have immutable values is:
for dictionary_name in ['dict1', 'dict2']: # The variable name dictionary for a string is very confusing, so changing that.
# create a new dictionary in memory
nested_dict[dictionary_name] = {k: v for k, v in key_dict.items()}
return nested_dict
If you have potentially values in key_dict that are mutable then you must make a deep copy. https://docs.python.org/3/library/copy.html
Solution 2:[2]
Your dict1
and dict2
keys are referencing to the same dict
object created before so any change performed on it will impact both key's values.
If you want to assign to your keys two different dicts
, you should copy it:
import copy
def create_nested():
key_dict = {}
for key in ['key1', 'key2']:
key_dict[key] = 0
nested_dict = {}
for dictionary in ['dict1', 'dict2']:
nested_dict[dictionary] = copy.deepcopy(key_dict)
deepcopy
will copy recursively all the elements inside your dict and will create an independent copy for you, so any change performed on it will impact only itself.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Neil |
Solution 2 | Gabio |