2011-03-13 106 views
7

自從我開始學習python以來,這種情況並不長,但我真的很想深入其中。並努力挖掘它。所以這裏是我已經研究了一段時間但尚未破解的任務:
我給出了嵌套字典和列表的混合組合(我們稱之爲「組合」),並且我需要實現函數,它允許訪問嵌套元素作爲對象屬性,也可以以某種方式將組合元素視爲可迭代的。這應該是這個樣子:將嵌套字典複雜轉換爲python中的對象

combination = { 
'item1': 3.14, 
'item2': 42, 
'items': [ 
     'text text text', 
     { 
      'field1': 'a', 
      'field2': 'b', 
     }, 
     { 
      'field1': 'c', 
      'field2': 'd', 
     }, 
     ] 
} 

def function(combination): 
    ... 

使
list(function(combination).items.field1) 會給:['a', 'c']
list(function(combination).item1)會給:[3.14]
編輯正如@FM提到的,我錯過了處理非字典元素的描述: list(function(combination).items[0]) >>>['text text text']


我試圖實現一個類(榮譽給Marc)幫我:

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

然後在功能使用它return Struct(**combination)
雖然是版本呃漂亮,它只是達到預期結果的第一步。
但隨着下一步需要更深入,它壓倒了我,我無法自己做到。
因此,我懇請您的幫助。

Michael。

+0

+1一個有趣的問題。但是你希望直接從'item'這樣的鍵跳到'field1'這樣的鍵,似乎處於一種緊張的狀態,保留了所有信息。後者是你的重要目標嗎?更具體地說,你如何設想訪問存儲在'items'(''text text text'')下的非dict元素? – FMc 2011-03-13 18:42:40

+0

@FM _combination_不是基於特定的真實信息方案,所以任何邏輯不一致都不意味着很多(如果我正確理解了你的話)。訪問非dict元素是重要的一點。我認爲'result.items'應該支持切片等。我會編輯問題。 – 2011-03-13 19:04:47

回答

6

如何:

class ComboParser(object): 
    def __init__(self,data): 
     self.data=data 
    def __getattr__(self,key): 
     try: 
      return ComboParser(self.data[key]) 
     except TypeError: 
      result=[] 
      for item in self.data: 
       if key in item: 
        try: 
         result.append(item[key]) 
        except TypeError: pass 
      return ComboParser(result) 
    def __getitem__(self,key): 
     return ComboParser(self.data[key]) 
    def __iter__(self): 
     if isinstance(self.data,basestring): 
      # self.data might be a str or unicode object 
      yield self.data 
     else: 
      # self.data might be a list or tuple 
      try: 
       for item in self.data: 
        yield item 
      except TypeError: 
       # self.data might be an int or float 
       yield self.data 
    def __length_hint__(self): 
     return len(self.data) 

這將產生:

combination = { 
    'item1': 3.14, 
    'item2': 42, 
    'items': [ 
     'text text text', 
     { 
      'field1': 'a', 
      'field2': 'b', 
      }, 
     { 
      'field1': 'c', 
      'field2': 'd', 
      }, 
     { 
      'field1': 'e', 
      'field3': 'f', 
      },   
     ] 
    } 
print(list(ComboParser(combination).item1)) 
# [3.1400000000000001] 
print(list(ComboParser(combination).items)) 
# ['text text text', {'field2': 'b', 'field1': 'a'}, {'field2': 'd', 'field1': 'c'}, {'field3': 'f', 'field1': 'e'}] 
print(list(ComboParser(combination).items[0])) 
# ['text text text'] 
print(list(ComboParser(combination).items.field1)) 
# ['a', 'c', 'e'] 
+0

+1:哇,非常巧妙地遞歸使用Martelli(和Doug Hudgeon的)'Bunch'集合/容器類。儘管我提出了一些小的美容建議,但是我會將'@ classmethod'轉換成''classmethod'',這會要求調用它成爲'result = Bunch.convert(combination)' - 以使事情變得更加明確。 – martineau 2011-03-13 19:13:37

+0

如何使'result.items'可迭代?我覺得這一定很簡單,但我仍然不擅長這一點。 – 2011-03-13 19:55:36

+0

_merge_之後的項目「文本文本文本」變爲不可用。對不起,我忘了從開始提到非字典可訪問性的重要性(稍後編輯)。 **一般**,你認爲有可能使_function_像一個發電機一樣行事嗎?我的意思是當被要求時,即時產生這些輸出結果,而不是預先評估。那會是_pythonic_? – 2011-03-13 20:36:13

1

我認爲基本上有兩種選擇,你可以追求在這裏:

  1. function轉換嵌套數據結構爲一系列鏈接在一​​起實現的協議支持list()dict()對象(對象必須實施多種功能,包括至少__iter____len__,__getitem__等)。要創建對象,您需要定義實現這些行爲的類並遞歸地組裝它們,或者使用type()即時創建類。

  2. 使function返回代理訪問底層數據結構的類。要實現允許非實際成員訪問成員屬性的類(即,執行function(combination).items),您可以覆蓋__getattr__。您將無法訪問「完整虛線路徑」,以便在此函數的任何單個調用中進行說話,因此它將不得不遞歸操作並在虛線路徑的每個級別返回其他實例。我相信這種方法比第一種方法更簡單。

1

什麼,你可能需要做的就是看你分配給每件商品的對象__dict__,看它本身是一個字典或迭代。

import types 
class Struct: 
    def __init__(self, **entries): 
     self.__dict__.update(entries) 
     for k,v in self.__dict__.items(): 
      if type(v) == types.DictType: 
       setattr(self, k, Struct(**v)) 

以便您使用遞歸排列。看起來是這樣的:

>>> b = Struct(a=1, b={'a':1}) 
>>> b.b.a 
1 
3

例如:

class Struct: 
    def __init__(self, **entries): 
     for key, value in entries.items(): 
      value2 = (Struct(**value) if isinstance(value, dict) else value) 
      self.__dict__[key] = value2 

entries = { 
    "a": 1, 
    "b": { 
     "c": { 
      "d": 2 
     } 
    } 
} 

obj = Struct(**entries) 
print(obj.a) #1 
print(obj.b.c.d) #2