PyYAML análise em objeto arbitrária
Pergunta
Eu tenho o seguinte programa Python 2.6 e definição YAML (usando PyYAML ):
import yaml
x = yaml.load(
"""
product:
name : 'Product X'
sku : 123
features :
- size : '10x30cm'
weight : '10kg'
"""
)
print type(x)
print x
O que resulta na seguinte saída:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}
É possível criar um objeto com campos de x
?
Gostaria do seguinte:
print x.features[0].size
Estou ciente de que é possível criar e instância de uma classe existente, mas isso não é o que eu quero para este cenário particular.
Editar:
- Atualização A parte confusa sobre um 'objeto fortemente tipado'.
- Acesso alterado para
features
a um indexador como sugerido Alex Martelli
Solução
Então você tem um dicionário com chaves de string e valores que podem ser números, dicionários aninhados, listas, e você gostaria de quebrar isso em uma instância que permite utilizar o acesso de atributo em vez de indexação dict, e "chamada com um índice" em vez de lista de indexação - não sei o que 'fortemente tipado' tem a ver com isso, ou por que você acha .features(0)
é melhor do que .features[0]
(como uma maneira mais natural para indexar uma lista!), mas, com certeza, é factível. Por exemplo, uma abordagem simples poderia 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])
Assim x = wrap(x['product'])
deve dar-lhe o seu desejo (porque você quer pular esse nível quando a sua lógica global, obviamente, exigem x.product.features(0).size
, eu não tenho idéia, mas é evidente que pular de melhor aplicado no ponto de chamada ao invés de hard-coded no classe de mensagens publicitárias ou a função de invólucro fábrica Acabei mostrado).
Editar : como o OP diz que quer features[0]
em vez de features(0)
, basta alterar as duas últimas linhas para
def __getitem__(self, n):
return wrap(self._data[n])
i., Definir __getitem__
(o método mágico subjacente indexação) em vez de __call__
(o método mágico subjacente instância-call).
A alternativa a "uma classe existente" (aqui, Fourie
) seria a criação de uma nova classe na mosca baseado em introspecção do dict embrulhado - viável, também, mas seriamente cinza-escuro, se não realmente preto , magia, e sem qualquer vantagem operacional real que eu possa pensar.
Se o OP pode esclarecer exatamente por isso que ele pode ser vontade depois de os picos de meta-programação de criação de classes na mosca, que vantagem ele acredita que ele pode estar recebendo dessa forma, etc, eu vou lhe mostrar como fazê-lo (e , provavelmente, eu também vou mostrar por que o ansiava-a vantagem vai não na verdade estar lá ;-). Mas a simplicidade é uma qualidade importante em qualquer esforço de programação, e usar "magia negra profunda" quando o código simples, simples como as obras acima muito bem, geralmente não é a melhor das ideias -!)