【问题标题】:How to make a custom YAML tag work with a sequence alias in pyyaml如何使自定义 YAML 标记与 pyyaml 中的序列别名一起使用
【发布时间】:2019-05-15 12:57:58
【问题描述】:

我有一个别名如下的 yaml 文件:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand ['user1', 'user2', 'user3', 'user4']

!rand 是一个自定义标签,它在 yaml 序列上调用 python 的 random.choice

我正在使用!rand 标签的以下实现:

import random
import yaml

def load_yaml(file):
    def rand_constructor(loader, node):
        value = loader.construct_sequence(node)
        return random.choice(value)

    yaml.add_constructor('!rand', rand_constructor)

    with open(file) as f:
        return yaml.load(f)

它按预期工作,userusers 获取一个随机值。 现在,当我将 !rand 与别名 *users 一起使用时,它会停止工作:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand *users

错误是:

File ../python3.6/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 "/temp/config.yml", line 5, column 3
expected <block end>, but found '<alias>'
  in "/temp/config.yml", line 6, column 18

如何使它与序列别名一起使用?

【问题讨论】:

    标签: python tags yaml pyyaml


    【解决方案1】:

    在 YAML 中,标签旨在定义内容类型。它们不是为处理内容而设计的。

    因此,您不能标记别名,因为别名只是对已在定义位置进行标记的内容的引用。在您的情况下,您的序列将根据YAML core schema 获得!!seq 标签。 YAML 不提供重新标记它的能力。

    话虽如此,您当然可以做一个解决方法:将!rand 参数包装在一个序列中:

    vars:
      users: &users ['user1', 'user2', 'user3', 'user4']
    
    refs:
      users: *users
      user: !rand [*users]
    

    然后您只需将代码更改为:

    import random
    import yaml
    
    def load_yaml(file):
        def rand_constructor(loader, node):
            value = loader.construct_sequence(node)
            return random.choice(value[0])
    
        yaml.add_constructor('!rand', rand_constructor)
    
        with open(file) as f:
            return yaml.load(f)
    

    但请注意,直接调用 !rand 如下所示:

    vars:
      users: &users ['user1', 'user2', 'user3', 'user4']
    
    refs:
      users: *users
      user: !rand [['user1', 'user2', 'user3', 'user4']]
    

    将其视为外部序列是函数!rand(它接受一个参数)的参数列表,而内部序列是该参数。

    【讨论】:

    • 根据您建议的更改,当我将其更改为 user2: !rand [*users] 时,结果 value 被设置为一个空列表 [[]] 并且回溯说:“IndexError:无法从空序列中选择"
    • 对不起,我没有测试这个。可能需要一些调整;也可能是别名解决得太晚而无法正常工作。我只是假设这基于我知道的其他 YAML 加载库。
    【解决方案2】:

    您还可以使您的列表可调用,并在每次需要随机用户时调用它:

    YAML = """
    user: &users !rand 
        - user1
        - user2
        - user3
        - user4
    """
    
    import random
    import yaml
    from collections.abc import Sequence
    
    class RandomizableList(Sequence):
        def __init__(self, items):
            self.items = items
        def __len__(self):
            return len(self.items)
        def __getitem__(self, value):
            return self.items[value]
        def __call__(self):
            return random.choice(self.items)
        def __repr__(self):
            return repr(self.items)
    
    def rand_constructor(loader, node):
        return RandomizableList(loader.construct_sequence(node))
    
    yaml.add_constructor('!rand', rand_constructor)
    result = yaml.load(YAML)
    for i in range(4):
        print(result['user']())
    print(result['user'])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-16
      • 1970-01-01
      • 2013-07-24
      • 2021-09-28
      • 2019-02-13
      • 2015-10-23
      • 1970-01-01
      相关资源
      最近更新 更多