2011-03-13 18 views
7

No hace mucho tiempo que comencé a aprender Python, pero realmente quiero excavar en él. Y cavar duro. Así que aquí hay una tarea que he estado estudiando por un tiempo, pero aún no he descifrado:
Me dieron una combinación mixta de diccionarios anidados y listas (llamémoslo "combinación"), y tengo que implementar función que permitirá el acceso a los elementos anidados como atributos de objeto, también tratar de alguna manera los elementos de combinación como iterables. Esto debe ser algo como esto:Complejos diccionarios anidados que se transforman en objetos en python

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

def function(combination): 
    ... 

modo que
list(function(combination).items.field1) dará: ['a', 'c'] y
list(function(combination).item1) dará: [3.14].
Editar Como se ha mencionado por @FM, que perdió descripción del manejo de elementos no dict: list(function(combination).items[0]) >>>['text text text']


Probé la implementación de una clase (felicitaciones a Marc) para que me ayude:

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

y luego utilizarlo en la función como return Struct(**combination)
Mientras que ser ver ingenioso, es solo el primer paso para el resultado deseado.
Pero como el siguiente paso necesita profundizar, me agobia y no puedo hacerlo por mí mismo.
Por lo tanto, le pido amablemente su ayuda.

Michael.

+0

+1 para una pregunta interesante. Pero su deseo de saltar directamente de una clave como 'items' a una clave como' field1' parece estar en tensión con un enfoque general que conserva toda la información. ¿Es este último un objetivo importante para ti? Más específicamente, ¿cómo prevé el acceso a los elementos no dict almacenados en 'items' (' 'text text text'')? – FMc

+0

@FM _combination_ no se basa en un esquema de información real específico, por lo que cualquier inconsistencia lógica no significa mucho (si lo entendí correctamente). El acceso a elementos no dict es un punto importante. Creo que 'result.items' debería soportar slicing y tal. Voy a editar la pregunta. –

Respuesta

6

¿Qué tal:

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) 

que produce:

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: Guau, uso recursivo muy ingenioso de la colección/contenedor de Martelli (y Doug Hudgeon) 'Bunch'. Sin embargo, una sugerencia cosmética menor, haría 'convert()' a '@ classmethod' que requeriría que la llamada se convirtiera en' result = Bunch.convert (combination) '- para hacer las cosas un poco más explícitas. – martineau

+0

¿Cómo puedo hacer 'result.items' iterable? Siento que debe ser simple, pero todavía no soy tan bueno en esto. –

+0

También los elementos '' text text text '' dejan de estar disponibles después de _merge_. Lamento que olvidé mencionar la importancia de la accesibilidad no dict desde el inicio (editado más adelante). ** En general **, ¿cree que es posible hacer que _function_ actúe como un generador? Me refiero a generar esos resultados de salida sobre la marcha cuando se les solicite, no pre-evaluar. ¿Sería eso _pythonic_? –

1

Creo que hay básicamente dos opciones que podría seguir aquí:

  1. Hacer function convertir la estructura de datos anidada en una serie de objetos vinculados entre sí que implementan los protocolos de apoyo list() y dict() (los objetos deben implementar una serie de funciones, que incluyen al menos __iter__, __len__, __getitem__, etc.). Para crear los objetos, debe definir las clases que implementan estos comportamientos y ensamblarlos recursivamente, o crear clases sobre la marcha usando type().

  2. Make function devuelve una clase que proxies acceso a la estructura de datos subyacente. Para implementar una clase que permita el acceso de atributo de miembro para miembros que no son realmente (es decir, haciendo function(combination).items), anula __getattr__. No podrá acceder a la "ruta completa punteada", por así decirlo, en una sola invocación de esta función, por lo que tendrá que operar recursivamente y devolver instancias adicionales en cada nivel de la ruta punteada. Creo que este enfoque será más simple que el primero.

1

Lo que es probable que tenga que hacer entonces es mirar a cada elemento que se asigna a la __dict__ para ver si él mismo es un diccionario o iterable su objeto.

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

para que esté utilizando una disposición recursiva. Se ve algo como esto:

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

Por ejemplo:

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 
Cuestiones relacionadas