1

Below is a sample code:

from collections import defaultdict

normal_dict = {
    "test": {
        "test1": 1,
        "test2": 2,
        "test3": {
            "test3_1": 1
        }
    }
}

default_dict = defaultdict(lambda: defaultdict(dict), normal_dict)

default_dict['test']['test4']['test4_1'] = 1 # Doesn't work


print(default_dict)

I get this error

Traceback (most recent call last):
  File "<string>", line 17, in <module>
KeyError: 'test4'

It seems like the previously non-existent key 'test4' is not being automatically added when I use the line default_dict['test']['test4']['test4_1'], for some reason. Theoretically, defaultdict should create the key test4 with another defaultdict as its default value.

  • 1
    The problem is that the defaultdict creation rules don't convert your fixed dictionaries from normal_dict into defaultdicts. If the initialization did try to coerce your input into its format then the ["test"]["test1"] would barf because 1 isn't a dict. I bet if you try you will see that default_dict['testX']['test4']['test4_1'] = 1 does work. And then if you just print(default_dict) you will see the difference between the 'test' entry and the 'testX' entry. – Malcolm Jul 18 '22 at 21:26
  • @Malcolm That makes sense. I guess the question now is how would you convert all the dictionaries inside `normal_dict` into defaultdicts? – bravesheeptribe Jul 18 '22 at 21:33
  • Does this answer your question? [Nested defaultdict of defaultdict](https://stackoverflow.com/questions/19189274/nested-defaultdict-of-defaultdict) – 0x5453 Jul 18 '22 at 21:46

1 Answers1

1

The Problem

The problem is that:

default_dict['test']

is:

{'test1': 1, 'test2': 2, 'test3': {'test3_1': 1}}

in other words is a dictionary not a defaultdict. Note that defaultdict provides a value (through the factory method) for a missing key, which is not the case at creation time in:

default_dict = defaultdict(lambda: defaultdict(dict), normal_dict)

It won't copy the values and "transform" them to defaultdict.

One solution

One approach, that transform every nested dictionary into a defaultdict is the following:

def nested_dd():
    # https://stackoverflow.com/a/19189356/4001592
    return defaultdict(nested_dd)


def convert_to_default(d):
    res = nested_dd()
    for key, value in d.items():
        if isinstance(value, dict):
            res[key] = convert_to_default(value)
        else:
            res[key] = value
    return res


result = convert_to_default(normal_dict)

result['test']['test4']['test4_1'] = 1
pprint.pprint(result)  # need to import pprint

Output

defaultdict(<function nested_dd at 0x7fd27008bd90>,
            {'test': defaultdict(<function nested_dd at 0x7fd27008bd90>,
                                 {'test1': 1,
                                  'test2': 2,
                                  'test3': defaultdict(<function nested_dd at 0x7fd27008bd90>,
                                                       {'test3_1': 1}),
                                  'test4': defaultdict(<function nested_dd at 0x7fd27008bd90>,
                                                       {'test4_1': 1})})})
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76