PyYAML中的构造函数中的接受锚(别名)

问题描述 投票:1回答:1

我需要为标记创建一个自定义构造函数。标签应接受列表以及列表的锚点。

示例,我想如何使用我的标签:

original: &value [1, 2, 3]
processed: !mytag *value

所以我为!mytag创建了一个基本的构造函数,该构造函数返回输入序列:

import yaml

def my_constructor(loader, node):
     return loader.construct_sequence(node)

yaml.Loader.add_constructor('!mytag', my_constructor)

但是当我尝试加载上面的YAML源时,出现错误:

>>> source = '''original: &value [1, 2, 3]
processed: !mytag *value'''

>>> yaml.load(source, yaml.Loader)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in t
  File "/usr/local/lib/python3.7/site-packages/yaml/__init__.py", line 114, in load
    return loader.get_single_data()
  File "/usr/local/lib/python3.7/site-packages/yaml/constructor.py", line 41, in get_single_data
    node = self.get_single_node()
  File "/usr/local/lib/python3.7/site-packages/yaml/composer.py", line 36, in get_single_node
    document = self.compose_document()
  File "/usr/local/lib/python3.7/site-packages/yaml/composer.py", line 55, in compose_document
    node = self.compose_node(None, None)
  File "/usr/local/lib/python3.7/site-packages/yaml/composer.py", line 84, in compose_node
    node = self.compose_mapping_node(anchor)
  File "/usr/local/lib/python3.7/site-packages/yaml/composer.py", line 127, in compose_mapping_node
    while not self.check_event(MappingEndEvent):
  File "/usr/local/lib/python3.7/site-packages/yaml/parser.py", line 98, in check_event
    self.current_event = self.state()
  File "/usr/local/lib/python3.7/site-packages/yaml/parser.py", line 439, in parse_block_mapping_key
    "expected <block end>, but found %r" % token.id, token.start_mark)
yaml.parser.ParserError: while parsing a block mapping
  in "test.yml", line 1, column 1
expected <block end>, but found '<alias>'
  in "test.yml", line 2, column 19

神奇的是,如果我用方括号将锚点引用包围,则可以使用:

>>> source = '''original: &value [1, 2, 3]
processed: !mytag [*value]'''

>>> yaml.load(source, yaml.Loader)
{'original': [1, 2, 3], 'processed': [[1, 2, 3]]}

但这不是我想要的,我需要将原始列表传递给构造函数,而不是双列表。

UPD:双重列表也不起作用。即使我返回它,它也会在结果中显示为原始列表,但是如果尝试从构造函数访问它,那么在那个阶段它只是一个空列表:

>>> source = '''original: &value [1, 2, 3]
... processed: !mytag [*value]'''
>>>
>>> def my_constructor(loader, node):
...     print(loader.construct_sequence(node))
...     return loader.construct_sequence(node)
...
>>> yaml.Loader.add_constructor('!mytag', my_constructor)
>>>
>>> yaml.load(source, yaml.Loader)
[[]]  # <--- this is the printed value
{'original': [1, 2, 3], 'processed': [[1, 2, 3]]}  # <--- this is the returned value

有人知道怎么做吗?

Python 3.7.6PyYAML 5.3

python yaml pyyaml
1个回答
1
投票

附加序列正是您想要的。

请注意,YAML标记描述了节点的type,并且不处理指令。别名是指现有节点,它们的类型已经为[[即使]],但它们没有显式标签(您的原始序列例如将在YAML核心架构下标记为!!seq)。现在,如果您希望标签的语义是“采用现有节点,并以某种方式对其进行转换”,则它描述了一个函数调用。函数调用本身就是一个结构,仅将其输入作为参数。因此,要对其进行正确建模,需要将参数放置在结构中,而顺序是执行此操作的最简单方法。您也可以通过映射来完成它:

original: &value [1, 2, 3] processed: !mytag {input: *value}

但是更冗长。

然后在您的构造函数中,从周围的结构中提取参数并对其进行处理。

Edit:

这是访问引用列表的概念证明。我不确定为什么您必须手动导航到外部节点,这可能是PyYAML错误。import yaml source = '''original: &value [1, 2, 3] processed: !mytag [*value]''' def my_constructor(loader, node): assert isinstance(node, yaml.SequenceNode) param = loader.construct_sequence(node.value[0], deep=True) print(param) # do something with the param here return param yaml.Loader.add_constructor('!mytag', my_constructor) print(yaml.load(source, yaml.Loader))
输出:

[1, 2, 3] {'original': [1, 2, 3], 'processed': [1, 2, 3]}

© www.soinside.com 2019 - 2024. All rights reserved.