python - 无法从 PyYAML 调用的构造函数中的参数构造对象

标签 python python-3.x yaml pyyaml

我有一个如下所示的 YAML 文件:

---
!Frog
    name: tree frog
    colour: green
    friends:
        - !Frog
          name: motorbike frog
        - !Frog
          name: blue arrow frog

以及一个使用PyYAML根据文件创建对象的python程序:

import yaml

class Frog():
    def __init__(self, name, colour="", friends=None):
        self.name = name
        self.colour = colour
        self.friends = {}
        if friends != None:
            for f in friends:
                self.friends[f.name] = f
        print("{}'s friends: {}".format(self.name, self.friends))

# Constructor for YAML
def frogConstructor(loader, node) :
    fields = loader.construct_mapping(node)
    return Frog(**fields)

yaml.add_constructor('!Frog', frogConstructor)

f = open("frog.yaml")
loaded = yaml.load(f)

正如您在上面的代码中看到的,我正在尝试根据 friends 参数创建一个 self.friends 字典(其中键是 Frog 的名字,该值是实际的 Frog 对象)到 __init__ 方法。但是,上面的代码会产生以下输出:

tree frog's friends: {}
motorbike frog's friends: {}
blue arrow frog's friends: {}

如您所见,self.friends 字典对于所有三只 Frog 都是空的,但是树蛙应该有两个 friend 。如果我只是让 self.friends = friends,它会按预期工作:self.friends 是 friend frogs 的列表。我做错了什么?

最佳答案

如果您执行 self.friends = friends,事情就会成功并不奇怪。您将一个最初为空的列表分配给 self.friends,该列表稍后会被 YAML 解析器追加。

如果您希望在构造您的 Frog() 之前填充该列表,您必须为 construct_mapping() 提供 deep=True 参数,这样做将确保首先创建底层非标量构造以及标量构造。

def frogConstructor(loader, node):
    fields = loader.construct_mapping(node, deep=True)
    return Frog(**fields)

然而,您的代码还有一些问题(但没有一个问题禁止上述功能运行):

  • 只有一个 None,所以使用 if friends is not None:if friends != None: 更合适
  • yaml.load 是不安全的,因此如果您无法完全控制您的输入,这可能意味着磁盘被删除(或更糟)。 PyYAML 不会为此警告您(在我的 ruamel.yaml 解析器中,您明确必须提供不安全的 Loader 以防止出现警告消息)。
  • 如果 tree frog 自恋到足以将自己视为自己的 friend ,或者如果它的 friend 之一将 tree frog 视为 friend ,您可能需要使用 anchor 和别名来表明这一点(而不只是在不同的 Frog 上使用相同的名称),这不适用于您正在使用的简单构造函数。
  • frogConstructor,作为函数名,不应该是驼峰式,使用frog_constructor代替。

由于上述原因,我不会使用 deep=True 参数,而是通过使用两阶段构造函数来寻求更安全、更完整的解决方案:

from ruamel import yaml

class Frog():
    def __init__(self, name):
        self.name = name

    def set_values(self, colour="", friends=None):
        self.colour = colour
        self.friends = {}
        if friends is not None:
            for f in friends:
                self.friends[f.name] = f
        print("{}'s friends: {}".format(self.name, self.friends))

    def __repr__(self):
        return "Frog({})".format(self.name)

# Constructor for YAML
def frog_constructor(loader, node):
    fields = loader.construct_mapping(node)
    frog = Frog(fields.pop('name'))
    yield frog
    frog.set_values(**fields)

yaml.add_constructor('!Frog', frog_constructor, yaml.SafeLoader)

f = open("frog.yaml")
loaded = yaml.safe_load(f)

用它你可以解析这个frog.yaml:

!Frog &tree_frog
    name: tree frog
    colour: green
    friends:
        - !Frog
          name: motorbike frog
          friends:
            - *tree_frog
        - !Frog
          name: blue arrow frog

得到输出:

tree frog's friends: {'blue arrow frog': Frog(blue arrow frog), 'motorbike frog': Frog(motorbike frog)}
motorbike frog's friends: {'tree frog': Frog(tree frog)}
blue arrow frog's friends: {}

关于python - 无法从 PyYAML 调用的构造函数中的参数构造对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42218249/

相关文章:

python - 使用字典查找 DataFrame 变量的值

python-3.x - 带有代理 : How to improve number of frames per second 的 PUB-SUB 网络

symfony1 - 如何在 Symfony 中创建自定义 yaml 配置文件

java - Play Framework 1.2.x 中的 ManyToMany 测试装置 (Yaml)

python - 如何在 python 中将单词插入列表?

python - Django 模板如何显示货币值,如 4.8p

python - 如何在 Matplotlib 中旋转表头?

python - Makefile 发现错误的 python 安装(python 2 而不是 python 3)

django - 如何测试 Django 装饰器?

yaml - 有没有办法从 YAML 多行键渲染新段落?