PyYAML разобрать в произвольном объекте - PullRequest
5 голосов
/ 14 марта 2010

У меня есть следующая программа на Python 2.6 и определение YAML (используется PyYAML ):

import yaml

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

         """
    )

print type(x)
print x


Что приводит к следующему выводу:
<type 'dict'><br> {'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}

Можно создать объект с полями из x?

Я бы хотел следующее:

print x.features[0].size

Я знаю, что можно создать экземпляр из существующего класса, но это не то, что я хочу для этого конкретного сценария.

Редактировать:

  • Обновлена ​​запутанная часть о "строго типизированном объекте".
  • Изменен доступ к features для индексатора, как предлагал Алекс Мартелли

1 Ответ

8 голосов
/ 14 марта 2010

Таким образом, у вас есть словарь со строковыми ключами и значениями, которые могут быть числами, вложенными словарями, списками, и вы хотите обернуть его в экземпляр, который позволяет использовать доступ к атрибутам вместо dict индексации, и "вызывать с помощью индекс "вместо индексации списка - не уверен, что" строго типизированный "имеет к этому отношение, или почему вы думаете, .features(0) лучше, чем .features[0] (такой более естественный способ индексировать список!), но Конечно, это возможно. Например, простой подход может быть следующим:

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

Так что x = wrap(x['product']) должно дать вам ваше желание (почему вы хотите пропустить этот уровень, когда ваша общая логика, очевидно, потребует x.product.features(0).size, я понятия не имею, но ясно, что пропуск лучше применять в точке вызова, а не жестко -кодировано в классе оболочки или в функции фабрики оболочки, которую я только что показал).

Редактировать : поскольку ОП говорит, что он хочет features[0] вместо features(0), просто измените две последние строки на

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

т.е. определить __getitem__ (магический метод, лежащий в основе индексации) вместо __call__ (магический метод, лежащий в основе вызова экземпляра).

Альтернативой «существующему классу» (здесь Fourie) было бы создание нового класса на лету на основе интроспекции упакованного диктата - возможно, тоже, но серьезно темно-серый, если не на самом деле черный , волшебный, без каких-либо реальных эксплуатационных преимуществ, о которых я только могу подумать.

Если ОП сможет уточнить, почему он жаждет после пиков метапрограммирования создания классов на лету, какое преимущество он считает, что он может получить этот путь и т. Д., Я покажу, как это сделать (и Вероятно, я также покажу, почему столь желанного преимущества на самом деле не будет , а ;-). Но простота является важным качеством в любом программировании, и использование «глубокой темной магии», когда простой, простой код, подобный приведенному выше, работает отлично, как правило, не лучшая идея! -)

...