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
Foi útil?

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top