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.