覆盖from_yaml以添加自定义YAML标签

时间:2018-08-24 15:06:53

标签: yaml pyyaml

重写from_yaml是否足以从类中注册标记,还是必须使用yaml.add_constructor(Class.yaml_tag, Class.from_yaml)?如果我不使用te add_constructor方法,则无法识别我的YAML标签。我所拥有的示例:

import yaml

class Something(yaml.YAMLObject):

    yaml_tag = u'!Something'

    @classmethod
    def from_yaml(cls,loader,node):
        # Set attributes to None if not in file
        values = loader.construct_mapping(node, deep=True)
        attr = ['attr1','attr2']
        result = {}
        for val in attr:
            try:
                result[val] = values[val]
            except KeyError:
                result[val] = None
        return cls(**result)

这足以使其正常工作吗?我对使用from_yaml和使用上述方法注册的其他构造函数感到困惑。我想我缺少一些基本知识,因为他们说:

  

子类化YAMLObject是定义标签,构造函数,   以及您班级的代表。您只需要覆盖   yaml_tag属性。如果要定义自定义构造函数,则   代表,重新定义from_yaml和to_yaml方法   相应地。

1 个答案:

答案 0 :(得分:1)

确实不需要显式注册:

import yaml

class Something(yaml.YAMLObject):
    yaml_tag = u'!Something'

    def __init__(self, *args, **kw):
        print('some_init', args, kw)

    @classmethod
    def from_yaml(cls,loader,node):
        # Set attributes to None if not in file
        values = loader.construct_mapping(node, deep=True)
        attr = ['attr1','attr2']
        result = {}
        for val in attr:
            try:
                result[val] = values[val]
            except KeyError:
                result[val] = None
        return cls(**result)

yaml_str = """\
test: !Something
   attr1: 1
   attr2: 2
"""

d = yaml.load(yaml_str)

给出:

some_init () {'attr1': 1, 'attr2': 2}

但是根本不需要使用PyYAML的load() 有文件证明是不安全的。如果您设置了safe_load类属性,则只能使用yaml_loader

import yaml

class Something(yaml.YAMLObject):
    yaml_tag = u'!Something'

    yaml_loader = yaml.SafeLoader

    def __init__(self, *args, **kw):
        print('some_init', args, kw)

    @classmethod
    def from_yaml(cls,loader,node):
        # Set attributes to None if not in file
        values = loader.construct_mapping(node, deep=True)
        attr = ['attr1','attr2']
        result = {}
        for val in attr:
            try:
                result[val] = values[val]
            except KeyError:
                result[val] = None
        return cls(**result)

yaml_str = """\
test: !Something
   attr1: 1
   attr2: 2
"""

d = yaml.safe_load(yaml_str)

因为它给出了相同的内容:

some_init () {'attr1': 1, 'attr2': 2}

(使用Python 3.6和Python 2.7完成)

__init__()元类的yaml.YAMLObject中进行注册:

class YAMLObjectMetaclass(type):
    """
    The metaclass for YAMLObject.
    """
    def __init__(cls, name, bases, kwds):
        super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
        if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
            cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
            cls.yaml_dumper.add_representer(cls, cls.to_yaml)

因此,也许您在某种程度上干扰了完整类的定义。像我一样,尝试从一个最小的实现开始,然后在需要的类上添加所需的功能,直到出现问题为止。