Complex conversion of nested dictionaries into objects in python

Not so long ago, I began to study python, but I really want to dig it out. And dig it out. So, here is a task that I have been studying for a while, but have not yet cracked:
I am given a mixed combination of nested dictionaries and lists (let's call it " combination "), and I need to implement a function that will allow access to nested elements as attributes of the object, and also somehow handle the combination how iterable. It should look something like this:

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

so list(function(combination).items.field1) will give: ['a', 'c'] and
list(function(combination).item1) will give: [3.14] .
Edit As mentioned in @FM, I skipped the description of processing non-dictical elements: list(function(combination).items[0]) →> ['text text text']


I tried implementing a class (kudos to Marc ) to help me:

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

and then using it in functions like return Struct(**combination)
Being very elegant, this is only the first step to the desired result.
But, since the next step should go deeper, it suppresses me, and I can not do it myself.
Therefore, I ask you for help.

Michael.

+7
source share
4 answers

What about:

 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) 

which gives:

 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'] 
+6
source

For example:

 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.bcd) #2 
+3
source

I think there are two options here:

  • Make a function convert the nested data structure into a series of interconnected objects that implement protocols to support list() and dict() (objects must implement a number of functions, including at least __iter__ , __len__ , __getitem__ , etc.). To create objects, you need to either define classes that implement these behaviors and reorganize them, or create classes on the fly using type() .

  • Make function return a class that proxies access to the underlying data structure. To implement a class that allows member attribute access for non-actual members (i.e., Executing function(combination).items ), you override __getattr__ . You will not be able to access the "full dotted path", so to speak, with any call to this function, so it will have to work recursively and return additional instances at each level of the point path. I believe that this approach will be simpler than the first.

+1
source

What you probably need to do is look at each element that you assign to your __dict__ object to make sure that it itself is dict or iterable.

 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)) 

so that you use a recursive scheme. It looks something like this:

 >>> b = Struct(a=1, b={'a':1}) >>> bba 1 
+1
source

All Articles