我在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]
""")
当前,我访问诸如
mydict["b"][0]["r"]
>>> 99
我想做的就是访问相同的信息
mydict.b[0].r
>>> 99
有没有办法像这样嵌套对象来加载YAML?还是我必须滚动自己的类并递归地将这些词典转换为嵌套对象?我猜想namedtuple可以使这变得容易一些,但是我更喜欢现成的解决方案。
答案 0 :(得分:1)
可以相对轻松地完成此操作,而无需更改输入文件。
自
dict
PyYAML的使用是硬编码的,无法修补,您不仅需要提供
行为类似dict的类,您还需要遍历篮球
PyYAML使用该类。即更改通常会构成SafeConstructor
的{{1}}
要使用该新类,请将其合并到新的Loader中,并使用PyYAML的dict
来使用该Loader:
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)
如果您需要能够处理YAML1.2,则应使用ruamel.yaml (免责声明:我是该程序包的作者),这使上面的操作稍微简单了
99
这还提供:
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)
(如果您的实际输入量很大,则加载数据的速度会明显加快)
答案 1 :(得分:0)
如果使用标记注释YAML文件的根节点,则可以定义从def isRepeat(inputString):
flag = False
print(inputString)# printing this perfectly
if len(inputString) % 2 !=0:
return False
else:
for i in range(len(inputString)//2):
print("x")
if inputString.count(inputString[i]) %2 == 0:
flag = True
else:
return False
return flag
output:
inputString = "2w2ww"
isRepeat(inputString)
2w2ww
False
派生的Python类来处理此as described in the PyYAML documentation。
但是,如果您希望自己的YAML保持标记的清洁,则可以自己构造嵌套类(摘自my answer to a similar question):
YAMLObject
但是,仅当您的YAML具有统一的结构时,此方法才有效。您通过在import yaml
class BItem:
def __init__(self, q, r, s):
self.q, self.r, self.s = q, r, s
class CItem:
def __init__(self, raw):
self.d, self.e, self.f = raw['d'], raw['e'], raw['f']
class Root:
def __init__(self, raw):
self.a = raw['a']
self.b = [BItem(i['q'], i['r'], i['s']) for i in raw['b']]
self.c = CItem(raw['c'])
mydict = Root(yaml.safe_load("""
a: 1
b:
- q: "foo"
r: 99
s: 98
- q: "bar"
r: 97
s: 96
c:
d: 7
e: 8
f: [9,10,11]
"""))
(b
,q
,r
,第一项s
,{{ 1}},第二项中的x
。我将YAML输入更改为具有相同的字段名称,因为对于不同的字段,此方法不起作用。我不确定您的YAML是否实际上是异类的,或者您只是偶然地这样做而已,例如。如果您的YAML实际上是异构的,则从dict访问访问项目是唯一可行的方法,因为从那时起,YAML文件中的键并不对应于类字段;它们是动态映射条目。
答案 2 :(得分:0)
找到了一个方便的库来完全满足我的需要: https://github.com/Infinidat/munch
import yaml
from munch import Munch
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]
""")
mymunch = Munch(mydict)
((我不得不编写一种简单的方法来将所有下标递归转换为孟子,但是现在我可以使用
>>> mymunch.b.q
"foo"
答案 3 :(得分:0)
像这样使用 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))