Just answering your second (last) question - it is popular topic, there exist many different projects (non-standard) that make your dictionary into dot notation object.
For example this one - attrdict. Install it through pip install attrdict.
Example of usage:
Try it online!
from attrdict import AttrDict
d = {'a': 1, 'b': [{'c': 2}, {'d': {'e': {'f': {5: {'g': 3}}}}}]}
ad = AttrDict(d)
print(ad.b[1].d.e.f(5).g) # 3
If you wonder how module like attrdict is implemented, then I wrote a very simple implementation of similar functionality (of course real attrdict should be more rich):
Try it online!
class AttrD(object):
def __init__(self, d = {}):
self.set_d(d)
def __getattr__(self, key):
return AttrD(self.get_or_create(key))
def __setattr__(self, key, value):
self.set_or_create(key, value)
def __getitem__(self, key):
return AttrD(self.get_or_create(key))
def __setitem__(self, key, value):
self.set_or_create(key, value)
def __call__(self, key):
return AttrD(self.get_or_create(key))
def __repr__(self):
return repr(self._d)
def to_obj(self):
return self._d
def set_d(self, d):
super(AttrD, self).__setattr__('_d', d)
def get_or_create(self, name):
if type(self._d) in (dict,) and name not in self._d:
self._d[name] = {}
if type(self._d) in (list, tuple) and len(self._d) <= name:
self.set_d(self._d + type(self._d)(
[None] * (name + 1 - len(self._d))))
return self._d[name]
def set_or_create(self, key, value):
self.get_or_create(key)
self._d[key] = value
ad = AttrD({'a': 1, 'b': [{'c': 2}, {'d': {'e': {'f': {5: {'g': 3}}}}}]})
ad.b[1].d.e.f(5).g = [4, 5, 6]
print(ad.b[1].d.e.f(5).g[2]) # 6
print(AttrD({'a': 123}).b.c) # Non-existent defaults to {}