PyYAML analizzare in oggetto arbitrario
Domanda
Ho il seguente programma Python 2.6 e la definizione YAML (utilizzando PyYAML ):
import yaml
x = yaml.load(
"""
product:
name : 'Product X'
sku : 123
features :
- size : '10x30cm'
weight : '10kg'
"""
)
print type(x)
print x
Che si traduce nella seguente output:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}
E 'possibile creare un oggetto con i campi da x
?
Vorrei il seguente:
print x.features[0].size
Sono consapevole che è possibile creare e l'istanza da una classe esistente, ma non è questo quello che voglio per questo scenario particolare.
Modifica
- Aggiornata la parte confusa circa un 'oggetto fortemente tipizzato'.
- accesso Cambiato in
features
a un indicizzatore come suggerito Alex Martelli
Soluzione
In modo da avere un dizionario con chiavi stringa e valori che possono essere numeri, dizionari nidificati, elenchi, e vuoi per avvolgere che in un'istanza che consente di utilizzare attribuire l'accesso al posto di dict indicizzazione, e "call con un indice" al posto di lista indicizzazione - non so cosa 'fortemente tipizzato' ha a che fare con questo, o perché si pensa .features(0)
è meglio di .features[0]
, ma, certo, è (un modo più naturale per indicizzare una lista!) fattibile. Ad esempio, un approccio semplice potrebbe essere:
def wrap(datum):
# don't wrap strings
if isinstance(datum, basestring):
return datum
# don't wrap numbers, either
try: return datum + 0
except TypeError: pass
return Fourie(datum)
class Fourie(object):
def __init__(self, data):
self._data = data
def __getattr__(self, n):
return wrap(self._data[n])
def __call__(self, n):
return wrap(self._data[n])
Quindi x = wrap(x['product'])
dovrebbe dare il vostro desiderio (il motivo per cui si desidera saltare quel livello quando la logica complessiva richiederebbe ovviamente x.product.features(0).size
, non ho idea, ma è chiaro che saltare di meglio applicata al punto di chiamata, piuttosto che hard-coded nel classe wrapper o la funzione wrapper fabbrica che ho appena mostrato).
Modifica : come l'OP dice di voler features[0]
piuttosto che features(0)
, basta cambiare le ultime due righe a
def __getitem__(self, n):
return wrap(self._data[n])
cioè., Definire __getitem__
(il metodo magico sottostante indicizzazione) anziché __call__
(il metodo magico sottostante esempio di guardia).
L'alternativa a "una classe esistente" (qui, Fourie
) sarebbe quello di creare una nuova classe al volo sulla base di introspezione del dict avvolto - fattibile, anche, ma sul serio grigio scuro, se non addirittura nero , la magia, e senza alcun reale vantaggio operativa che mi viene in mente.
Se l'OP può chiarire esattamente il motivo per cui egli può essere brama dopo i picchi di meta-programmazione di creazione di classi al volo, che vantaggio crede potrebbe essere sempre in quel modo, ecc, vi mostrerò come farlo (e , probabilmente, farò anche mostri il perché l'agognato-per il vantaggio sarà non , infatti, essere lì ;-). Ma la semplicità è una qualità importante in qualsiasi sforzo di programmazione, e l'utilizzo di "magia nera profondo" quando pianura, codice semplice come le opere di cui sopra bene, non è generalmente la migliore delle idee -!)