PyYaml:无法从构造函数 class' __init__ 方法中访问嵌套的实例属性

PyYaml: cannot access nested instance attributes from within constructor class' __init__ method

我正在尝试使用 PyYaml add_constructor() 函数来 return 来自 YAML 节点的 class 实例。

构造函数class:

class PrintAttributes():
    
    def __init__(self, a, b):
        self._a = a
        self.b = b
        print(f"instance a => attr: {self._a}, arg: {a}")
        print(f"instance b => attr: {self.b}, arg: {b}")
    
    def __repr__(self):
        return "%s(a=%r, b=%r)" % (self.__class__.__name__, self._a, self.b)

手动实例化时似乎按预期工作:

>>> PrintAttributes(3,4)

instance a => attr: 3, arg: 3
instance b => attr: 4, arg: 4

PrintAttributes(a=3, b=4)

这是我将要使用的两个 YAML 文件:

simple_yaml = """
a: 3
b: 4
"""

nested_yaml = """
a: 
  value: 3
b:
  value: 4
"""

这表明我可以使用 yaml.load() 方法正确加载它们:

io_simple = io.StringIO(simple_yaml)
io_nested = io.StringIO(nested_yaml)
>>> d_simple = yaml.load(io_simple, Loader=yaml.SafeLoader)
>>> print(d_simple)
{'a': 3, 'b': 4}

>>> d_nested = yaml.load(io_nested, Loader=yaml.SafeLoader)
>>> print(d_nested)
{'a': {'value': 3}, 'b': {'value': 4}}

再一次,如果我将上述字典传递给 class 构造函数,它会正常工作:

>>> print(PrintAttributes(**d_simple))
instance a => attr: 3, arg: 3
instance b => attr: 4, arg: 4
PrintAttributes(a=3, b=4)

>>> print(PrintAttributes(**d_nested))
instance a => attr: {'value': 3}, arg: {'value': 3}
instance b => attr: {'value': 4}, arg: {'value': 4}
PrintAttributes(a={'value': 3}, b={'value': 4})

现在,让我们创建用于定义自定义“构造函数”的辅助函数,并将加载的 YAML 映射到自定义 class PrintAttributes:

def PrintAttributes_constructor(loader: yaml.SafeLoader, node: yaml.nodes.MappingNode) -> PrintAttributes:
    """Construct a PrintAttributes dictionary"""
    return PrintAttributes(**loader.construct_mapping(node))
    
def get_loader():
    """Add constructors to PyYAML loader."""
    loader = yaml.SafeLoader
    loader.add_constructor('!PrintAttributes', PrintAttributes_constructor)
    return loader

这些是上面的 YAML 文件,带有用于调用构造函数的附加标签 !PrintAttributes

simple_yaml_construct = """
tag_here: !PrintAttributes
 a: 3
 b: 4
"""

nested_yaml_construct = """
tag_here: !PrintAttributes
  a: 
    value: 3
  b:
    value: 4
"""

当我使用简单的(即未嵌套的)YAML 文件时,这可以正常工作:

>>> print(yaml.load(io_simple_construct, Loader=get_loader()))
instance a => attr: 3, arg: 3
instance b => attr: 4, arg: 4
{'tag_here': PrintAttributes(a=3, b=4)}

虽然,当我尝试 load/parse 嵌套的 YAML 文件时,我无法在实例初始化期间访问参数:

>>> print(yaml.load(io_nested_construct, Loader=get_loader()))
instance a => attr: {}, arg: {}
instance b => attr: {}, arg: {}
{'tag_here': PrintAttributes(a={'value': 3}, b={'value': 4})}

我的目标是在实例初始化期间访问这些值以更新 class 变量。 我可以使用第一个 YAML 文件执行此操作,但我无法访问第二个 YAML 文件的嵌套值。

不过,我确实在实例的表示中看到了嵌套值(即 __repr__())。所以,它们肯定是满载的。

另一件需要注意的事情是 class 的名称 return 由 __repr__() 编辑如何从 PrintAttributes 更改为 tag_here

我是不是遗漏了什么,或者只是一些 PyYAML 限制?

谢谢

您需要附加参数 deep=True:

    return PrintAttributes(**loader.construct_mapping(node, deep=True))

如果没有这个,子字典 ab 将在您的构造函数 returns 之后填充。这就是为什么它们在 __init__ 中包含空值,但在构建完成后包含正确的值。