Recursively access dict via attributes as well as index access? Recursively access dict via attributes as well as index access? python python

Recursively access dict via attributes as well as index access?


Here's one way to create that kind of experience:

class DotDictify(dict):    MARKER = object()    def __init__(self, value=None):        if value is None:            pass        elif isinstance(value, dict):            for key in value:                self.__setitem__(key, value[key])        else:            raise TypeError('expected dict')    def __setitem__(self, key, value):        if isinstance(value, dict) and not isinstance(value, DotDictify):            value = DotDictify(value)        super(DotDictify, self).__setitem__(key, value)    def __getitem__(self, key):        found = self.get(key, DotDictify.MARKER)        if found is DotDictify.MARKER:            found = DotDictify()            super(DotDictify, self).__setitem__(key, found)        return found    __setattr__, __getattr__ = __setitem__, __getitem__if __name__ == '__main__':    life = {'bigBang':               {'stars':                   {'planets': {}  # Value changed from []                   }               }           }    life = DotDictify(life)    print(life.bigBang.stars.planets)  # -> []    life.bigBang.stars.planets.earth = {'singleCellLife' : {}}    print(life.bigBang.stars.planets)  # -> {'earth': {'singleCellLife': {}}}


Below another implementation of a nested attribute dictionary (inspired by the answer of Curt Hagenlocher, stripped down to the essential):

class AttrDict(dict):    """ Nested Attribute Dictionary    A class to convert a nested Dictionary into an object with key-values    accessible using attribute notation (AttrDict.attribute) in addition to    key notation (Dict["key"]). This class recursively sets Dicts to objects,    allowing you to recurse into nested dicts (like: AttrDict.attr.attr)    """    def __init__(self, mapping=None):        super(AttrDict, self).__init__()        if mapping is not None:            for key, value in mapping.items():                self.__setitem__(key, value)    def __setitem__(self, key, value):        if isinstance(value, dict):            value = AttrDict(value)        super(AttrDict, self).__setitem__(key, value)        self.__dict__[key] = value  # for code completion in editors    def __getattr__(self, item):        try:            return self.__getitem__(item)        except KeyError:            raise AttributeError(item)    __setattr__ = __setitem__

This works in both Python 2 and 3:

life = AttrDict({'bigBang': {'stars': {'planets': {}}}})life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}life.bigBang.stars.planets.earth.multiCellLife = {'reptiles': {}, 'mammals': {}}print(life.bigBang.stars.planets.earth)# -> {'singleCellLife': {}, 'multiCellLife': {'mammals': {}, 'reptiles': {}}}

Converting KeyError into AttributeError in __getattr__ is required in Python3 such that hasattr works also in case the attribute is not found:

hasattr(life, 'parallelUniverse')# --> False


There is a package doing exactly what you want and also something more and it is called Prodict.

from prodict import Prodictlife_dict = {'bigBang':                {'stars':                    {'planets': []}                }            }life = Prodict.from_dict(life_dict)print(life.bigBang.stars.planets)# prints []# you can even add new properties dynamicallylife.bigBang.galaxies = []

PS: I'm the author of the Prodict.