我目前正在加载像这样的YAML文件
import yaml
yaml.load('''level0:
stuff: string0
level1:
stuff: string1
level2: ...''')
上面的代码创建嵌套词典。
我想创建FancyDict
个对象的嵌套实例,而不是创建嵌套字典。
class FancyDict(collections.MutableMapping):
def __init__(self, *args, **kwargs):
for name in kwargs:
setattr(self, name, kwargs[name])
关于Constructors, Representer, Resolvers的部分 似乎没有解决我希望全局覆盖类的情况 所有字典的构造,而不是特殊标记的字典。
我只需要一个可以被称为对象(节点?)的钩子来创建/完成
有没有一种简单的方法可以执行此操作,还是应该遍历嵌套的词典yaml.load
返回给我并自行修复?
答案 0 :(得分:1)
那个钩子不存在,构造的类型在construct.BaseConstructor.construct_mapping()
中被硬编码。
解决这个问题的方法是制作你自己的构造函数,并根据你自己的加载器,并将其作为load()
的选项:
import sys
import collections
import ruamel.yaml as yaml
yaml_str = """\
level0:
stuff: string0
level1:
stuff: string1
level2: ...
"""
from ruamel.yaml.reader import Reader
from ruamel.yaml.scanner import Scanner
from ruamel.yaml.parser import Parser
from ruamel.yaml.composer import Composer
from ruamel.yaml.constructor import SafeConstructor
from ruamel.yaml.resolver import Resolver
from ruamel.yaml.nodes import MappingNode
class FancyDict(collections.MutableMapping):
def __init__(self, *args, **kwargs):
for name in kwargs:
setattr(self, name, kwargs[name])
# provide the missing __getitem__, __setitem__, __delitem__, __iter__, and __len__.
class MyConstructor(SafeConstructor):
def construct_mapping(self, node, deep=False):
res = SafeConstructor.construct_mapping(self, node, deep)
assert isinstance(res, dict)
return FancyDict(**res)
class MyLoader(Reader, Scanner, Parser, Composer, MyConstructor, Resolver):
def __init__(self, stream, version=None):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
MyConstructor.__init__(self)
Resolver.__init__(self)
data = yaml.load(yaml_str, Loader=MyLoader)
当你运行它时,你会得到一个错误,即FancyDict是一个无法实例化的抽象类:
TypeError:无法使用抽象方法
实例化抽象类FancyDict__delitem__
,__getitem__
,__iter__
,__len__
,__setitem__
我猜你的真实FancyDict
已经实现了。
ruamel.yaml是一个支持YAML 1.2的YAML库(我推荐使用它,但后来我是该软件包的作者)。 PyYAML仅支持(大部分)YAML 1.1。更有问题的是,对于Python2和Python3,它有不同的constructor.py
文件,因此你可能无法在PyYAML中输入上述代码。
答案 1 :(得分:0)
我找到了一个实际适用于 PyYaml 的解决方案。
class Loader(yaml.FullLoader):
def construct_yaml_map(self, node):
data = MyDictionaryClass()
yield data
value = self.construct_mapping(node)
data.update(value)
Loader.add_constructor(
'tag:yaml.org,2002:map',
Loader.construct_yaml_map
)
使用 this answer 中的解决方案的问题是 PyYaml 将映射转换回 construct_yaml_map
函数上的字典。由于为 SafeLoader 添加了自定义 add_constructor
,因此仅替换子类中的此函数是不够的,因此您可以覆盖它以将新的 construct_yaml_map
用于您的类。