Recursively access dict via attributes as well as index access?

前端 未结 5 1493
隐瞒了意图╮
隐瞒了意图╮ 2020-12-02 06:36

I\'d like to be able to do something like this:

from dotDict import dotdictify

life = {\'bigBang\':
           {\'stars\':
               {\'planets\': []}
         


        
相关标签:
5条回答
  • 2020-12-02 06:59

    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': {}}}
    
    0 讨论(0)
  • 2020-12-02 06:59

    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
    
    0 讨论(0)
  • 2020-12-02 07:03

    Here is another solution:

    from typing import Dict, Any
    
    class PropertyTree: pass
    
    def dict_to_prop_tree(yaml_config: Dict[str, Any]) -> PropertyTree:
        tree = PropertyTree()
        for key, value in yaml_config.items():
            if type(value) == dict:
                setattr(tree, key, dict_to_obj_tree(value))
            elif type(value) == list:
                setattr(tree, key, [dict_to_obj_tree(v) for v in value])
            else:
                setattr(tree, key, value)
        return tree
    

    Then in the python console:

    d={'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': {'g': 6}, 'h': {}, 'j': 7}}
    tree=dict_to_prop_tree(d)
    tree.a
    tree.c.f.g
    

    prints the correct values

    0 讨论(0)
  • 2020-12-02 07:03
    #!/usr/bin/env python3
    # _*_ coding: utf-8 _*_
    
    # Author: Xingbang Jiang
    # E-mail: 1278561590@qq.com
    # HomePage: http://www.xingbangsharing.tech
    
    class Dotsdict(dict):
        def __init__(self, args, **kwargs):
            super(Dotsdict, self).__init__(args, **kwargs)
            for obj in [args, kwargs]:
                for k, v in obj.items():
                    if isinstance(v, dict):
                        v = Dotsdict(v)
                    self.__setitem__(k, v)
    
        def __setitem__(self, key, val):
            super(Dotsdict, self).__setitem__(key, val)
            # self.__dict__[key] = val
    
        def __delitem__(self, key):
            super(Dotsdict, self).__delitem__(key)
            # del self.__dict__[key]
    
        def __getitem__(self, key):
            return super(Dotsdict, self).__getitem__(key)
    
        def __missing__(self, key):
            dots = Dotsdict()
            self.__setitem__(key, dots)
            return dots
    
        __setattr__, __delattr__, __getattr__ = __setitem__, __delitem__, __getitem__
    
    # ===================================================================
    
    
    d = {'k': 'v', 'x': {'y': 'z', 'p': 'q', }, }
    print(type(d))
    print(d)
    
    dd = Dotsdict(d, i='j')
    print(type(dd))
    print(dd)
    
    print('========================================')
    
    dd.a = 'b'
    dd.x.m = 'n'
    print(dd.x.y)
    
    del dd.x['p']
    print(dd)
    print(len(dd))
    
    0 讨论(0)
  • 2020-12-02 07:11

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

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

    PS: I'm the author of the Prodict.

    0 讨论(0)
提交回复
热议问题