class DotDictionary(dict):

    def __init__(self, init_value):
        if not isinstance(init_value, dict):
            raise TypeError('Expected dict, got {}'.format(type(init_value)))

        self._dict_to_self(init_value)
        super(DotDictionary, self).__init__()

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, val):
        if isinstance(val, dict):
            val = DotDictionary(val)
        self[name] = val

    def __setitem__(self, name, val):
        if isinstance(val, dict):
            val = DotDictionary(val)

        super(DotDictionary, self).__setitem__(name, val)

    def __delattr__(self, name):
        del self[name]

    def _dict_to_self(self, dct):
        for key, value in dct.items():
            if isinstance(value, (tuple, list)):
                for i, _ in enumerate(value):
                    if isinstance(value[i], dict):
                        value[i] = DotDictionary(value[i])
            self[key] = value


d = {'a': 0,
     'b': {'aa': 1, 'bb': {'aaa': 2, 'bbb': 3, 'ccc': {'aaaa': 4}}},
     'c': [5, {'aaaaa': 6, 'bbbbb': 7, 'ccccc': [{'aaaaaa': 8}]}]}

dd = DotDictionary(d)
dd['dict'] = {'hey': 'hey'}
dd.dict.hello = {'hello': 123}

print(dd.a)
print(dd.b.aa)
print(dd.c[1].aaaaa)
print(dd.c[1].ccccc[0].aaaaaa)
print(dd.dict.hey)
print(dd.dict.hello.hello)
