Question

J'ai le programme suivant Python 2.6 et la définition YAML (en utilisant PyYAML ):

import yaml

x = yaml.load(
    """
        product:
           name     : 'Product X'
           sku      : 123
           features :
             - size    :  '10x30cm'
               weight  :  '10kg'

         """
    )

print type(x)
print x


Ce qui se traduit par la sortie suivante:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}

Il est possible de créer un objet avec des champs de x?

Je voudrais à ce qui suit:

print x.features[0].size

Je sais qu'il est possible de créer et d'instance d'une classe existante, mais ce n'est pas ce que je veux pour ce scénario particulier.

Modifier

  • Mise à jour la partie source de confusion au sujet d'un « objet fortement typé ».
  • Modification accès à features à un indexeur comme suggéré Alex Martelli
Était-ce utile?

La solution

Vous avez donc un dictionnaire avec les touches de chaîne et les valeurs qui peuvent être des nombres, des dictionnaires imbriqués, listes, et que vous souhaitez envelopper que dans une instance qui vous permet d'utiliser l'attribut accès au lieu de l'indexation dict et « appeler avec un indice » en lieu et place de l'indexation de la liste - pas sûr de ce « fortement typé » doit faire avec cela, ou pourquoi vous pensez que .features(0) est meilleur que .features[0], mais, bien sûr, il est (de manière plus naturelle pour indexer une liste!) réalisable. Par exemple, une approche simple pourrait être:

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

Alors x = wrap(x['product']) devrait vous donner votre souhait (pourquoi vous voulez sauter ce niveau lorsque votre logique globale exigerait évidemment x.product.features(0).size, je ne sais pas, mais il est clair que l'amélioration de sauter appliqué au point d'appel plutôt que codés en dur dans le classe wrapper ou la fonction de l'usine d'emballage que je viens de vous montrer).

Modifier : comme l'OP dit qu'il ne veut features[0] plutôt que features(0), il suffit de changer les deux dernières lignes à

  def __getitem__(self, n):
    return wrap(self._data[n])

i.e.., Définir __getitem__ (la méthode magique indexation sous-jacente) au lieu de __call__ (la méthode magique instance d'appel sous-jacent).

L'alternative à « une classe existante » (ici, Fourie) serait de créer une nouvelle classe à la volée en fonction de l'enveloppé dict introspectant - possible aussi, mais sérieusement gris foncé, sinon noir , la magie, et sans aucun avantage opérationnel réel que je peux penser.

Si l'OP peut préciser exactement pourquoi il peut être hankering après les pics de méta-programmation de la création de classes à la volée, quel avantage il croit qu'il pourrait obtenir de cette façon, etc, je vais vous montrer comment le faire (et , probablement, je vais aussi montrer pourquoi le imploré-pour avantage sera pas en fait là ;-). Mais la simplicité est une qualité importante dans toute entreprise de programmation, et en utilisant la « magie noire profonde » lorsque le code clair, simple comme les travaux ci-dessus très bien, est généralement pas la meilleure des idées -)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top