PyYAML analizar en objeto arbitrario
Pregunta
Tengo el siguiente programa Python 2.6 y definición YAML (usando PyYAML ):
import yaml
x = yaml.load(
"""
product:
name : 'Product X'
sku : 123
features :
- size : '10x30cm'
weight : '10kg'
"""
)
print type(x)
print x
Lo que resulta en la siguiente salida:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}
Es posible crear un objeto con campos de x
?
Me gustaría a lo siguiente:
print x.features[0].size
Soy consciente de que es posible crear e instancia de una clase existente, pero eso no es lo que quiero para este escenario particular.
Editar
- Se ha actualizado la parte confusa acerca de un 'objeto fuertemente tipado'.
- Acceso a
features
cambiado a un controlador paso a paso como se sugiere Alex Martelli
Solución
Así que tienes un diccionario con claves de cadena y valores que pueden ser números, diccionarios, listas anidadas, y desea que envuelva en una instancia que le permite utilizar atributos de acceso en lugar de indexación dict, y "llamar con un índice" en lugar de la lista de indexación - no sé qué 'inflexible' tiene que ver con esto, o por qué cree que .features(0)
es mejor que .features[0]
, pero, claro, que es (tal manera más natural para indexar una lista!) factible. Por ejemplo, un enfoque sencillo podría ser:
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])
Así x = wrap(x['product'])
debe darle su deseo (¿por qué desea omitir ese nivel cuando su lógica global, obviamente, requieren x.product.features(0).size
, no tengo ni idea, pero es evidente que la omisión de una mejor aplicación en el punto de escala en lugar de no modificable en el clase de contenedor o la función de fábrica envoltorio que acabo de muestra).
Editar : como dice el OP sí quiere features[0]
en lugar de features(0)
, basta con cambiar las dos últimas líneas a
def __getitem__(self, n):
return wrap(self._data[n])
es decir., Definir __getitem__
(el método magia indexación subyacente) en lugar de __call__
(el método magia subyacente ejemplo-llamada).
La alternativa a "una clase existente" (en este caso, Fourie
) sería la creación de una nueva clase sobre la marcha basado en la introspección de la dict envuelto - factible, también, pero en serio de color gris oscuro, si no realmente negro , la magia, y sin ninguna ventaja operativa real que se me ocurre.
Si el PO puede aclarar exactamente por qué puede ser anhelando los picos de los meta-programación de la creación de clases sobre la marcha, lo que él cree que la ventaja pudo conseguir de esa manera, etc, voy a mostrar cómo hacerlo (y , probablemente, yo también voy a mostrar por qué el ansiado-para la ventaja será no en el hecho de estar allí ;-). Pero la simplicidad es una cualidad importante en cualquier empresa de programación, y el uso de "profunda magia negra" cuando llano, código sencillo como lo anterior funciona muy bien, por lo general no es la mejor de las ideas -)