PyYAML выполняет синтаксический анализ в произвольный объект
Вопрос
У меня есть следующая программа на Python 2.6 и определение YAML (с использованием Пиямл):
import yaml
x = yaml.load(
"""
product:
name : 'Product X'
sku : 123
features :
- size : '10x30cm'
weight : '10kg'
"""
)
print type(x)
print x
Что приводит к следующему результату:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}
Можно создать объект с полями из x
?
Я хотел бы сделать следующее:
print x.features[0].size
Я знаю, что можно создать экземпляр из существующего класса, но это не то, что я хочу для данного конкретного сценария.
Редактировать:
- Обновлена запутанная часть о "строго типизированном объекте".
- Изменен доступ к
features
индексатору , как предложил Алекс Мартелли
Решение
Итак, у вас есть словарь со строковыми ключами и значениями, которые могут быть числами, вложенными словарями, списками, и вы хотели бы обернуть это в экземпляр, который позволяет вам использовать доступ к атрибутам вместо индексации 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
) было бы создать новый класс "на лету" на основе самоанализа обернутого dict - тоже возможно, но серьезно темно-серого, если не на самом деле Черный, магия, и без какого-либо реального оперативного преимущества, о котором я могу думать.
Если ОП сможет точно объяснить, почему он может стремиться к вершинам метапрограммирования в создании классов "на лету", какое преимущество, по его мнению, он мог бы получить таким образом и т.д., я покажу, как это сделать (и, вероятно, я также покажу, почему желанное преимущество будет нет на самом деле будь там;-).Но простота - важное качество в любом начинании программиста, и использовать "глубокую темную магию", когда простой код, подобный приведенному выше, работает просто отлично, как правило, не самая лучшая идея!-)