2012-06-15 17 views
6

Tengo una configuración YAML que se parece a:¿Cómo cargar un archivo pyYAML y acceder a él usando atributos en lugar de usar la notación del diccionario?

config: 
- id: foo 
- name: bar 
content: 
- run: xxx 
- remove: yyy 

estoy usando módulo de Python YAML para cargarlo pero quiero acceder a él en mejores maneras como:

stream = open(filename) 
config = load(stream, Loader=Loader) 
print(config['content']) 

Lo que yo quiero es poder hacer: print(config.content).

+1

Duplicado de http://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary – Justin

+0

@Justin: Esto no es un duplicado de esta pregunta, porque simplemente puede parchear el cargador YAML para crear objetos de cualquier clase que desee en lugar de instancias de 'dict'. –

Respuesta

5

Puede utilizar notación de objetos con los diccionarios utilizando la siguiente clase, como se discutió en this respuesta:

class DictAsMember(dict): 
    def __getattr__(self, name): 
     value = self[name] 
     if isinstance(value, dict): 
      value = DictAsMember(value) 
     return value 

Esta clase de acción:

>>> my_dict = DictAsMember(one=1, two=2) 
>>> my_dict 
{'two': 2, 'one': 1} 
>>> my_dict.two 
2 

Editar Esto funciona de forma recursiva con sub diccionarios, por ejemplo:

>>> my_dict = DictAsMember(one=1, two=2, subdict=dict(three=3, four=4)) 
>>> my_dict.one 
1 
>>> my_dict.subdict 
{'four': 4, 'three': 3} 
>>> my_dict.subdict.four 
4 
+0

Creo que esto no funciona recursivamente, como se puede imaginar, el mismo problema se repite para otras entradas en el árbol de configuración. – sorin

+0

Si entiendo lo que quiere decir, esto funciona de manera recursiva. ¿Mi edición responde tu punto? – Chris

+1

¿Por qué esta (la respuesta más simple que existe) no sube un millón de veces? ¡Hay docenas de preguntas similares/duplicadas que responden a esto de una manera tan complicada! – brandonscript

4

La forma más sencilla de hacerlo es, probablemente, sobrescribir el constructor YAML para tag:yaml.org,2002:map, por lo que devuelve una clase de diccionario personalizada en lugar de un diccionario normal.

import yaml 

class AttrDict(object): 
    def __init__(self, attr): 
     self._attr = attr 
    def __getattr__(self, attr): 
     try: 
      return self._attr[attr] 
     except KeyError: 
      raise AttributeError 

def construct_map(self, node): 
    # WARNING: This is copy/pasted without understanding! 
    d = {} 
    yield AttrDict(d) 
    d.update(self.construct_mapping(node)) 

# WARNING: We are monkey patching PyYAML, and this will affect other clients!  
yaml.add_constructor('tag:yaml.org,2002:map', construct_map) 

YAML = """ 
config: 
    - id: foo 
    - name: bar 
content: 
    - run: xxx 
    - remove: yyy 
""" 

obj = yaml.load(YAML) 

print(obj.config[0].id) # prints foo 

Tenga en cuenta que esto va a romper todo lo demás en el proceso que utiliza YAML, si se espera que todo funcione de la manera normal de Python. Puede usar un cargador personalizado, pero personalmente encuentro que la documentación PyYAML es un poco laberíntica, y parece que los efectos colaterales son globales y contagiosos como una regla en lugar de una excepción.

Has sido advertido.

Como alternativa, si el esquema es relativamente estática podría escribir sus propias clases y deserializar a aquellos (por ejemplo, class Config con id y name propiedades). Sin embargo, probablemente no valdría la pena el costo del código adicional.

+0

Simplificado y mejorado esto en https://gist.github.com/ktaragorn/9cf6d368378b0f65a3a0. No hay monopatching y funciona de forma anidada –

+0

@KarthikT: falla en los diccionarios anidados dentro de las matrices. –

+0

Ni siquiera había considerado ese caso de uso ... ¿sucede eso a menudo cuando se consideran archivos de configuración? –

Cuestiones relacionadas