Woots
Woots

Reputation: 3

Nesting dictionaries in python

I am new to Python and I am trying to build a simple REST api with flask. I do not understand why this results in an error:

boxes = {}
counter = 1
for box in ListOfBoxes:

    boxes['boxes'][counter]['ID']= box.ID
    boxes['boxes'][counter]['name']= box.name 
    boxes['boxes'][counter]['state']= box.state
    boxes['boxes'][counter]['directory']= box.directory
    boxes['boxes'][counter]['provider']= box.provider
    counter +=1 

return jsonify(boxes)

While this works:

boxes = {}
counter = 1
for box in ListOfBoxes:

    boxes['boxes'] = {}
    boxes['boxes'][counter] = []
    boxes['boxes'][counter].append({'ID': box.ID, 'name':box.name, 'state': box.state, 'directory': box.directory, 'provider': box.provider})
    counter +=1 

return jsonify(boxes)

Error being:

    File "./flask/app.py", 
line 16, in get_boxes boxes['boxes'][counter]['ID']= box.ID KeyError: 'boxes'

When googling and stackoverflowing for answers, mostly the second piece of code is used. At least that's what I understand from it.

Upvotes: 0

Views: 137

Answers (3)

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24164

A simple way to implement a default tree is to use a defaultdict:

from collections import defaultdict

def Tree():
    return defaultdict(Tree)

Then:

boxes = Tree()

boxes['boxes'][5]['provider']= box.provider

Upvotes: 1

Dan Lowe
Dan Lowe

Reputation: 56687

In your first example, you are trying to access uninitialized values.

boxes = {}
counter = 1
for box in ListOfBoxes:

    boxes['boxes'][counter]['ID']= box.ID

Here boxes is an empty dict, but boxes['boxes'] does not exist yet, nor do any of the other levels you're trying to access.

The second example is illustrating how to initialize each new layer in your data structure, before attempting to use it.

# Create a new Dictionary, assign to a key in Dictionary 'boxes'
boxes['boxes'] = {}

# Create a new List, assign to a key in Dictionary we just created
boxes['boxes'][counter] = []

# Now that this List exists, we can use .append() to add to it
boxes['boxes'][counter].append({'ID': box.ID, 'name':box.name, 'state': box.state, 'directory': box.directory, 'provider': box.provider})

You received a KeyError because you tried to access a dictionary key that did not yet exist: boxes['boxes'] was never initialized to have a value; therefore, it does not exist.

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477794

Well the problem is that boxes does not contain a key 'boxes'. Since you here **do not set boxes['boxes'], but you get the element, and perform operations on that element.

A way to resolve it, is of course immediately insert an element with that key in the dictionary boxes, so:

boxes = {'boxes':[]}

Nevertheless this is still not enough, since again you get boxes['boxes'][counter] to perform operations on that object. So again, you will have to construct a dictionary.

You can however rewrite your code elegantly using list-comprehension and dictionary literals to:

result = [{'ID': box.ID,'name':box.name,'state':box.state,
           'directory':box.directory,'provider':box.provider}
          for box in ListOfBoxes]
boxes = {'boxes':result}
return jsonify(boxes)

If box is an object and it contains only the fields discussed above, you can even use the following one-liner:

return jsonify({'boxes':[box.__dict__ for box in ListOfBoxes]})

since __dict__ is the dictionary of the object (containing the name of the fields mapped to its values). But this only works under the above discussed conditions.

Upvotes: 1

Related Questions