Convert nested Python dict to object?

后端 未结 30 1930
时光取名叫无心
时光取名叫无心 2020-11-22 09:28

I\'m searching for an elegant way to get data using attribute access on a dict with some nested dicts and lists (i.e. javascript-style object syntax).

For example:

相关标签:
30条回答
  • Here is another way to implement SilentGhost's original suggestion:

    def dict2obj(d):
      if isinstance(d, dict):
        n = {}
        for item in d:
          if isinstance(d[item], dict):
            n[item] = dict2obj(d[item])
          elif isinstance(d[item], (list, tuple)):
            n[item] = [dict2obj(elem) for elem in d[item]]
          else:
            n[item] = d[item]
        return type('obj_from_dict', (object,), n)
      else:
        return d
    
    0 讨论(0)
  • 2020-11-22 10:13

    I know there's already a lot of answers here already and I'm late to the party but this method will recursively and 'in place' convert a dictionary to an object-like structure... Works in 3.x.x

    def dictToObject(d):
        for k,v in d.items():
            if isinstance(v, dict):
                d[k] = dictToObject(v)
        return namedtuple('object', d.keys())(*d.values())
    
    # Dictionary created from JSON file
    d = {
        'primaryKey': 'id', 
        'metadata': 
            {
                'rows': 0, 
                'lastID': 0
            }, 
        'columns': 
            {
                'col2': {
                    'dataType': 'string', 
                    'name': 'addressLine1'
                }, 
                'col1': {
                    'datatype': 'string', 
                    'name': 'postcode'
                }, 
                'col3': {
                    'dataType': 'string', 
                    'name': 'addressLine2'
                }, 
                'col0': {
                    'datatype': 'integer', 
                    'name': 'id'
                }, 
                'col4': {
                    'dataType': 'string', 
                    'name': 'contactNumber'
                }
            }, 
            'secondaryKeys': {}
    }
    
    d1 = dictToObject(d)
    d1.columns.col1 # == object(datatype='string', name='postcode')
    d1.metadata.rows # == 0
    
    0 讨论(0)
  • 2020-11-22 10:13

    Old Q&A, but I get something more to talk. Seems no one talk about recursive dict. This is my code:

    #!/usr/bin/env python
    
    class Object( dict ):
        def __init__( self, data = None ):
            super( Object, self ).__init__()
            if data:
                self.__update( data, {} )
    
        def __update( self, data, did ):
            dataid = id(data)
            did[ dataid ] = self
    
            for k in data:
                dkid = id(data[k])
                if did.has_key(dkid):
                    self[k] = did[dkid]
                elif isinstance( data[k], Object ):
                    self[k] = data[k]
                elif isinstance( data[k], dict ):
                    obj = Object()
                    obj.__update( data[k], did )
                    self[k] = obj
                    obj = None
                else:
                    self[k] = data[k]
    
        def __getattr__( self, key ):
            return self.get( key, None )
    
        def __setattr__( self, key, value ):
            if isinstance(value,dict):
                self[key] = Object( value )
            else:
                self[key] = value
    
        def update( self, *args ):
            for obj in args:
                for k in obj:
                    if isinstance(obj[k],dict):
                        self[k] = Object( obj[k] )
                    else:
                        self[k] = obj[k]
            return self
    
        def merge( self, *args ):
            for obj in args:
                for k in obj:
                    if self.has_key(k):
                        if isinstance(self[k],list) and isinstance(obj[k],list):
                            self[k] += obj[k]
                        elif isinstance(self[k],list):
                            self[k].append( obj[k] )
                        elif isinstance(obj[k],list):
                            self[k] = [self[k]] + obj[k]
                        elif isinstance(self[k],Object) and isinstance(obj[k],Object):
                            self[k].merge( obj[k] )
                        elif isinstance(self[k],Object) and isinstance(obj[k],dict):
                            self[k].merge( obj[k] )
                        else:
                            self[k] = [ self[k], obj[k] ]
                    else:
                        if isinstance(obj[k],dict):
                            self[k] = Object( obj[k] )
                        else:
                            self[k] = obj[k]
            return self
    
    def test01():
        class UObject( Object ):
            pass
        obj = Object({1:2})
        d = {}
        d.update({
            "a": 1,
            "b": {
                "c": 2,
                "d": [ 3, 4, 5 ],
                "e": [ [6,7], (8,9) ],
                "self": d,
            },
            1: 10,
            "1": 11,
            "obj": obj,
        })
        x = UObject(d)
    
    
        assert x.a == x["a"] == 1
        assert x.b.c == x["b"]["c"] == 2
        assert x.b.d[0] == 3
        assert x.b.d[1] == 4
        assert x.b.e[0][0] == 6
        assert x.b.e[1][0] == 8
        assert x[1] == 10
        assert x["1"] == 11
        assert x[1] != x["1"]
        assert id(x) == id(x.b.self.b.self) == id(x.b.self)
        assert x.b.self.a == x.b.self.b.self.a == 1
    
        x.x = 12
        assert x.x == x["x"] == 12
        x.y = {"a":13,"b":[14,15]}
        assert x.y.a == 13
        assert x.y.b[0] == 14
    
    def test02():
        x = Object({
            "a": {
                "b": 1,
                "c": [ 2, 3 ]
            },
            1: 6,
            2: [ 8, 9 ],
            3: 11,
        })
        y = Object({
            "a": {
                "b": 4,
                "c": [ 5 ]
            },
            1: 7,
            2: 10,
            3: [ 12 , 13 ],
        })
        z = {
            3: 14,
            2: 15,
            "a": {
                "b": 16,
                "c": 17,
            }
        }
        x.merge( y, z )
        assert 2 in x.a.c
        assert 3 in x.a.c
        assert 5 in x.a.c
        assert 1 in x.a.b
        assert 4 in x.a.b
        assert 8 in x[2]
        assert 9 in x[2]
        assert 10 in x[2]
        assert 11 in x[3]
        assert 12 in x[3]
        assert 13 in x[3]
        assert 14 in x[3]
        assert 15 in x[2]
        assert 16 in x.a.b
        assert 17 in x.a.c
    
    if __name__ == '__main__':
        test01()
        test02()
    
    0 讨论(0)
  • 2020-11-22 10:14

    Update: In Python 2.6 and onwards, consider whether the namedtuple data structure suits your needs:

    >>> from collections import namedtuple
    >>> MyStruct = namedtuple('MyStruct', 'a b d')
    >>> s = MyStruct(a=1, b={'c': 2}, d=['hi'])
    >>> s
    MyStruct(a=1, b={'c': 2}, d=['hi'])
    >>> s.a
    1
    >>> s.b
    {'c': 2}
    >>> s.c
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MyStruct' object has no attribute 'c'
    >>> s.d
    ['hi']
    

    The alternative (original answer contents) is:

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)
    

    Then, you can use:

    >>> args = {'a': 1, 'b': 2}
    >>> s = Struct(**args)
    >>> s
    <__main__.Struct instance at 0x01D6A738>
    >>> s.a
    1
    >>> s.b
    2
    
    0 讨论(0)
  • 2020-11-22 10:15

    This also works well too

    class DObj(object):
        pass
    
    dobj = Dobj()
    dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}
    
    print dobj.a
    >>> aaa
    print dobj.b
    >>> bbb
    
    0 讨论(0)
  • 2020-11-22 10:16

    Typically you want to mirror dict hierarchy into your object but not list or tuples which are typically at lowest level. So this is how I did this:

    class defDictToObject(object):
    
        def __init__(self, myDict):
            for key, value in myDict.items():
                if type(value) == dict:
                    setattr(self, key, defDictToObject(value))
                else:
                    setattr(self, key, value)
    

    So we do:

    myDict = { 'a': 1,
               'b': { 
                  'b1': {'x': 1,
                        'y': 2} },
               'c': ['hi', 'bar'] 
             }
    

    and get:

    x.b.b1.x 1

    x.c ['hi', 'bar']

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