Это можно сделать относительно легко и без изменения входного файла.
Поскольку использование dict
PyYAML жестко запрограммировано и не может быть исправлено, вам нужно не только указывать в форме словаКласс, который ведет себя так, как вы хотите, вы также должны пройти через обручи, чтобы PyYAML использовал этот класс.Т.е. измените SafeConstructor
, который обычно создает dict
для использования этого нового класса, включите его в новый Loader и используйте PyYAML's load
, чтобы использовать этот Loader:
import sys
import yaml
from yaml.loader import Reader, Scanner, Parser, Composer, SafeConstructor, Resolver
class MyDict(dict):
def __getattr__(self, name):
return self[name]
class MySafeConstructor(SafeConstructor):
def construct_yaml_map(self, node):
data = MyDict()
yield data
value = self.construct_mapping(node)
data.update(value)
MySafeConstructor.add_constructor(
u'tag:yaml.org,2002:map', MySafeConstructor.construct_yaml_map)
class MySafeLoader(Reader, Scanner, Parser, Composer, MySafeConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
MySafeConstructor.__init__(self)
Resolver.__init__(self)
yaml_str = """\
a: 1
b:
- q: "foo"
r: 99
s: 98
- x: "bar"
y: 97
z: 96
c:
d: 7
e: 8
f: [9,10,11]
"""
mydict = yaml.load(yaml_str, Loader=MySafeLoader)
print(mydict.b[0].r)
, что дает:
99
Если вам нужно уметь работать с YAML1.2, вы должны использовать ruamel.yaml (заявление об отказе: я автор этого пакета), что немного упрощает приведенное выше описание
import ruamel.yaml
# same definitions for yaml_str, MyDict
class MySafeConstructor(ruamel.yaml.constructor.SafeConstructor):
def construct_yaml_map(self, node):
data = MyDict()
yield data
value = self.construct_mapping(node)
data.update(value)
MySafeConstructor.add_constructor(
u'tag:yaml.org,2002:map', MySafeConstructor.construct_yaml_map)
yaml = ruamel.yaml.YAML(typ='safe')
yaml.Constructor = MySafeConstructor
mydict = yaml.load(yaml_str)
print(mydict.b[0].r)
что также дает:
99
(и если ваш реальный ввод велик, загрузка данных должна быть заметно быстрее)