Search code examples
pythondictionarynesteddefaultdict

Python: defaultdict(dict) how to create nested dictionary?


I need to grab strings from all files in a directory and record them somehow, so I tried to use defaultdict to create this, but I'm having difficulty figuring out how to progressively add to each layer of the dictionary. Essentially, what the dictionary should look like is this:

Filename

Bundle

Info

Bundle

Info

Filename

Bundle

Info

etc I have the info as a list so I can just append anything I need onto the list, but when I run what I have here, I end up getting a single bundle and info for each file name. It seems like the update() function is replacing the values inside and I'm not sure how to make it keep adding and then creating a newer dictionary for each Bundle. Any help is appreciated, and sorry for any confusion.


import collections
import os

devices = collections.defaultdict(lambda: collections.defaultdict(dict))
# bundles = collections.defaultdict(dict)

for filename in os.listdir('.'):
    if os.path.isfile(filename):
        if '.net' in filename:
            dev = open(filename)

            for line in dev:

                line = line.strip()
                values = line.split(' ')

                if values[0] == 'interface':
                    bundle_name = values[1]
                    if '/' in bundle_name:
                        pass
                    else:
                        if len(values) == 2:
                            ur_device = devices[filename]
                            ur_device.update(
                                {
                                    'bundle_name': bundle_name,
                                    'bundle_info': [],
                                }
                            )

                if 'secondary' in values:
                    pass
                elif values[0] == 'ipv4' and values[1] == 'address' and len(values) == 4:
                    ur_device['bundle_info'].append(
                        {
                            'ip_address': values[2],
                            'subnet_mask': values[3],
                        }
                    )

            dev.close()

Solution

  • A dictionary is something that has a key and a value, any time you put something into a dictionary that has the same key, it will replace that value. For instance:

    dictionary = {}
    # Insert value1 at index key1
    dictionary["key1"] = "value1"
    
    # Overwrite the value at index key1 to value2
    dictionary["key1"] = "value2"
    
    print(dictionary["key1"]) # prints value2
    

    There are a few things that don't make sense about your code, but I suggest you use the above syntax for actually adding things to the dictionary (rather than the update method as that is used for adding a list of key/value pairs into a dictionary).

    Below I marked up some suggestions to your code with #**'s

    import collections
    import os
    
    #** I'm guessing you want to put your devices into this dict, but you never put anything into it
    devices = collections.defaultdict(lambda: collections.defaultdict(dict))
    # bundles = collections.defaultdict(dict)
    
    for filename in os.listdir('.'):
        if os.path.isfile(filename):
            if '.net' in filename:
                dev = open(filename)
    
                for line in dev:
    
                    line = line.strip()
                    values = line.split(' ')
                    #** declare bundle_name out here, it is better form since we will make use of it in the immediate scopes below this
                    bundle_name = ''
    
                    if values[0] == 'interface':
                        bundle_name = values[1]
                        if '/' in bundle_name:
                            pass
                        else:
                            if len(values) == 2:
                                #** This is assuming you populated devices somewhere else??
                                devices[filename][bundle_name] = []
    
                    if 'secondary' in values:
                        pass
                    elif values[0] == 'ipv4' and values[1] == 'address' and len(values) == 4:
                        #** ur_device was not declared in this scope, it is a bad idea to use it here, instead index it out of the devices dict
                        #** it also might be worth having a check here to ensure devices[filename] actually exists first
                        devices[filename][bundle_name].append(
                            {
                                'ip_address': values[2],
                                'subnet_mask': values[3],
                            }
                        )
    
                dev.close()
    

    ** Edit **

    Looking at your question, you need to have multiple bundles for each filename. But your dictionary structure is not sufficiently detailed and you don't seem to provide the code for how you initialized devices with data.

    Basically, you should think about what keys each level of the dictionary is going to have, not just what values.

    devices (filename: device)
    device (bundle_name?: info) <-- You are missing this dictionary
    info (list that you append details to)

    I changed the above code with my best guess as to what you intended.