2012-06-15 5 views
6

Ho una configurazione YAML che assomiglia:Come caricare un file pyYAML e accedervi usando gli attributi invece di usare la notazione del dizionario?

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

Sto usando modulo Python YAML di caricarlo ma voglio accedervi in ​​modo migliore come:

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

Quello che voglio è quello di essere in grado di fare: print(config.content).

+1

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

+0

@Justin: Questo non è un duplicato di questa domanda, perché puoi semplicemente applicare una patch al caricatore YAML per creare oggetti di qualunque classe desideri invece di istanze di 'dict'. –

risposta

5

È possibile utilizzare la notazione oggetto con dizionari che utilizzano la seguente classe, come discusso in this risposta:

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

Questa classe in azione:

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

Modifica Questo funziona in modo ricorsivo con sub- dizionari, ad esempio:

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

Penso che questo non funzioni in modo ricorsivo, come puoi immaginare lo stesso problema si ripete per le altre voci nella struttura di configurazione. – sorin

+0

Se capisco cosa intendi, funziona in modo ricorsivo. La mia modifica risponde al tuo punto? – Chris

+1

Perché questa (la risposta più semplice là fuori) non è revocata un milione di volte? Ci sono dozzine di domande simili/duplicate che rispondono in modo così complicato! – brandonscript

4

Il modo più semplice per eseguire questa operazione è probabilmente sovrascrivere il costruttore YAML per tag:yaml.org,2002:map in modo che restituisca una classe dizionario personalizzata anziché un dizionario normale.

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 

Si noti che questo si romperà tutto il resto del processo che utilizza YAML, se si aspetta di tutto per lavorare nel modo Python normale. È possibile utilizzare un caricatore personalizzato, ma personalmente trovo la documentazione PyYAML un po 'labirintica, e sembra che gli effetti collaterali siano globali e contagiosi come una regola piuttosto che un'eccezione.

Sei stato avvisato.

In alternativa, se lo schema è relativamente statico si potrebbe scrivere le proprie classi e deserializzare a quelli (ad esempio, con class Configid e name proprietà). Probabilmente non varrebbe la pena il costo del codice extra, comunque.

+0

Semplificato e migliorato in https://gist.github.com/ktaragorn/9cf6d368378b0f65a3a0. Non esiste un monkeypatching e funziona anche in modo annidato –

+0

@KarthikT: non riesce a trovare i dizionari annidati all'interno degli array. –

+0

Non avevo nemmeno preso in considerazione il caso d'uso. Succede spesso quando si prendono in considerazione i file di configurazione? –

Problemi correlati