2010-06-13 87 views
23

我希望能夠做這樣的事情:通過屬性遞歸訪問字典以及索引訪問?

from dotDict import dotdictify 

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

dotdictify(life) 

# This would be the regular way: 
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}} 
# But how can we make this work? 
life.bigBang.stars.planets.earth = {'singleCellLife': {}} 

#Also creating new child objects if none exist, using the following syntax: 
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles':{},'mammals':{}} 

我的動機是提高代碼的簡潔,如果可能使用類似的語法爲Javascript訪問JSON對象進行有效的跨平臺開發。 (我也用Py2JS和類似。)

+0

'life.bigBang.stars.planets'定義一個'list',所以分配給它的'.earth'屬性將導致一個'AttributeError:'列表'對象沒有屬性'地球'。我猜你可能想要在'life'字典的定義中使用'{'planets':{}}''(如接受的答案所示)。 – martineau 2017-10-17 19:29:03

回答

41

這裏有一個方法來創建這樣的經歷:

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': {}}} 
+5

調用字典.__ setitem __(self,key,value)是我失蹤的遞歸技巧!代碼中有相當優雅的代碼:) – 2010-06-13 06:39:50

+1

你(和@Luke)應該使用'super(dotdictify,self).__ setitem __(key,value)'而不是'dict .__ setitem __(self,key,value)'(否則你的子類有多個祖先會遇到麻煩)。 (除了builtins)類應該以大寫字母開頭 – 2014-06-20 09:44:42

+0

@TobiasKienzler感謝您指出邊緣案例 – 2014-06-20 14:56:27

0

下面的嵌套屬性字典(由柯特Hagenlocher的答案靈感的另一個實現剝離下來基本):

class AttrDict(dict): 
    """ Nested Attribute Dictionary 

    A class to convert a nested Dictionary into an object with key-values 
    accessibly using attribute notation (AttrDict.attribute) in addition to 
    key notation (Dict["key"]). This class recursively sets Dicts to objects, 
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr) 
    """ 

    def __init__(self, mapping): 
     super(AttrDict, self).__init__() 
     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) 

    def __getattr__(self, item): 
     try: 
      return self.__getitem__(item) 
     except KeyError: 
      raise AttributeError(item) 

    __setattr__ = __setitem__ 

這個工作在兩個Python 2和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': {}}} 

轉換成KeyError異常在AttributeError的在__getattr__ Python3需要使得hasattr作品也萬一屬性中沒有找到:

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