如何使自定义YAML标签与pyyaml中的序列别名一起使用

时间:2018-12-14 09:14:48

标签: python tags yaml pyyaml

我有一个别名如下的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的{​​{1}}时,它将停止工作:

*users

错误是:

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

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

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

2 个答案:

答案 0 :(得分: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(带有一个参数)的参数列表,内部序列是该参数。

答案 1 :(得分:0)

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

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'])