【问题标题】:Overriding from_yaml to add custom YAML tag覆盖 from_yaml 以添加自定义 YAML 标记
【发布时间】:2019-01-31 02:23:28
【问题描述】:

覆盖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 方法 相应地。

【问题讨论】:

    标签: yaml pyyaml


    【解决方案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(),它是 记录为不安全。如果您设置了yaml_loader 类属性,则可以只使用safe_load

    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 完成)

    注册在yaml.YAMLObject的元类的__init__()中完成:

    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)
    

    因此,也许您以某种方式干扰了完整类定义中的初始化。尝试像我一样从一个最小的实现开始,然后在你的类上添加你需要的功能,直到出现问题。

    【讨论】:

    • @cap 我的代码中没有任何地方的代表,上面是我运行以获得结果的实际代码,仅此而已。仅当您想要进行(自定义)转储时才需要代表。
    猜你喜欢
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 2017-08-19
    • 2021-12-19
    相关资源
    最近更新 更多