Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a namespace with a dict of dicts

Currently I am using json to save a dict to a config file. I load this to turn it into a dict then turn it into a SimpleNamespace because I prefer dot notation to access the settings. To do this, I load it as in this example:

import json
from types import SimpleNamespace
SETTINGS = json.load(open("config.json", 'r'))
SETTINGS = SimpleNamespace(**SETTINGS)

However, as I am currently loading the dict into a SimpleNamespace it is not loading the sub dicts within the config file. So for example if I do:

SETTINGS.server_info.port

I get the error:

AttributeError: 'dict' object has no attribute 'port'

I was wondering how I load all dicts into the namespace as name spaces so that I am able to use dot notation all the way down the dictionary.

like image 477
Ryan Schaefer Avatar asked May 23 '18 14:05

Ryan Schaefer


1 Answers

It is not necessary to recursively apply the transformation to the data returned by json.load. You can simply ask json.load to return SimpleNamespace instances instead of dictionaries by providing an object_hook method to the json.load call. This method "will be called with the result of every JSON object decoded and its return value will be used in place of the given dict." (from the docs).

The simplest object_hook might look like:

def dict_to_sns(d):
    return SimpleNamespace(**d)

For example, given the following input:

{
  "settings": {
    "foo": {
      "number": 4,
      "size": "large"
    },
    "bar": {
      "color": "orange",
      "widgets": [
        "gizmo",
        "gadget",
        "thing"
      ]
    }
  }
}

We can do the following:

>>> import json
>>> from types import SimpleNamespace
>>> def dict_to_sns(d):
...     return SimpleNamespace(**d)
... 
>>> with open('settings.json') as fd:
...     data = json.load(fd, object_hook=dict_to_sns)
... 
>>> data
namespace(settings=namespace(bar=namespace(color='orange', widgets=['gizmo', 'gadget', 'thing']), foo=namespace(number=4, size='large')))
>>> data.settings.foo
namespace(number=4, size='large')
>>> data.settings.foo.number
4
>>> data.settings.bar.widgets
['gizmo', 'gadget', 'thing']
like image 141
larsks Avatar answered Sep 23 '22 08:09

larsks