在Python中,将YAML作为嵌套对象而不是字典加载
我在YAML中有一个配置文件,当前使用YAML.safe_load作为字典加载。为了便于编写代码,我更愿意将其作为一组嵌套对象加载。参考字典的更深层次是很麻烦的,并且使代码更难阅读 例如:在Python中,将YAML作为嵌套对象而不是字典加载,python,yaml,Python,Yaml,我在YAML中有一个配置文件,当前使用YAML.safe_load作为字典加载。为了便于编写代码,我更愿意将其作为一组嵌套对象加载。参考字典的更深层次是很麻烦的,并且使代码更难阅读 例如: import yaml mydict = yaml.safe_load(""" a: 1 b: - q: "foo" r: 99 s: 98 - x: "bar" y: 97 z: 96 c: d: 7 e: 8 f: [9,10,11] """) 目前,我访问的项目包括 myd
import yaml
mydict = yaml.safe_load("""
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["b"][0]["r"]
>>> 99
我想做的是像这样访问相同的信息
mydict.b[0].r
>>> 99
有没有办法像这样将YAML作为嵌套对象加载?或者我必须滚动我自己的类并递归地将这些字典翻转成嵌套对象?我猜namedtuple可以让这更容易一些,但我更喜欢一个现成的解决方案。如果您用标记注释YAML文件的根节点,您可以定义从
YAMLObject
派生的Python类来处理这个问题
但是,如果希望YAML不受标记的影响,可以自己构造嵌套类(取自):
然而,只有当YAML的结构是均匀的时,这种方法才有效。通过在
b
列表中使用不同名称的字段(第一项中的q
,r
,s
;第二项中的x
,y
,z
)给出了异构结构。我将YAML输入更改为具有相同的字段名,因为对于不同的字段,这种方法不起作用。我不确定您的YAML是否实际上是异构的,或者您只是意外地将其作为一个示例。如果您的YAML实际上是异构的,那么通过dict访问访问条目是唯一可行的方法,因为YAML文件中的键与类字段不对应;它们是动态映射项。这可以相对轻松地完成,而且无需更改输入文件
自从
dict
PyYAML使用的是硬编码的,无法修补,您不仅需要提供
一个像dict这样的类,如果你想表现的话,你还必须经历各种困难才能做到
Pyaml使用该类。即,更改通常会构造一个dict的SafeConstructor
要使用该新类,请将其合并到新的加载程序中,并使用PyYAML的load
使用该加载程序:
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
(如果您的实际输入量很大,加载数据的速度应该会明显加快)找到了一个方便的库,可以完全满足我的需要: (我必须编写一个简单的方法来递归地将所有子部分转换为munches,但现在我可以使用
>>> mymunch.b.q
"foo"
使用SimpleNamespace的作品是什么样的:
import yaml
import json
from types import SimpleNamespace
dict = yaml.safe_load(definition)
obj = SimpleNamespace(**dict)
唯一的问题是它不支持嵌套/递归字典。
为了实现完整的对象树转换,我使用:
dict = yaml.safe_load(definition)
obj = json.loads(json.dumps(dict), object_hook=lambda d: SimpleNamespace(**d))
@roganjosh你能证明你的说法是没有办法做到这一点吗?我认为应该是
munch.munchify(mydict)
,所以它也会递归地为嵌套的dict创建“munch”对象。
import yaml
import json
from types import SimpleNamespace
dict = yaml.safe_load(definition)
obj = SimpleNamespace(**dict)
dict = yaml.safe_load(definition)
obj = json.loads(json.dumps(dict), object_hook=lambda d: SimpleNamespace(**d))