首页 > 解决方案 > 在 Python 中从 YAML 创建自定义类的对象

问题描述

我有类定义:

类定义

class Task(object):
    def __init__(self, name, *depends):
        self.__name    = name
        self.__depends = set(depends)

    @property
    def name(self):
        return self.__name

    @property
    def depends(self):
        return self.__depends

其余代码使用如下示例定义:

a = Task("a", "b")
b = Task("b")

nodes = (a, b)

我试图通过在返回字典的单独函数中解析 YAML 来动态创建这些对象data,然后创建所有对象:

Yaml 文件

a:
  foo:
   - "bar"
  graph_dep:
  - "b"
b:
  foo:
   - "bar"

我相信我能够通过函数实现类的创建ab对象Task

def format_input(data):
    services = data.keys()
    holder = {Task(name=name) for name in services}
    return holder

q1:如何将它们组合起来以获取nodes对象并获得与使用示例定义相同的结果?q2:如何从字符串中获取值graph_dep并将它们添加到holder = {Task(name=name) for name in services}字符串中?

对不起Python中的新手问题:)

标签: pythonclassyaml

解决方案


通常,如果您不需要 YAML 的原始结构,则无需将其加载到原生 Python 类型中。相反,您应该只将它们加载到 YAML 节点图中,然后从中加载您想要的本机结构:

import yaml

input = """
a:
  foo:
   - "bar"
  graph_dep:
  - "b"
b:
  foo:
   - "bar"
"""

class Task(object):
  def __init__(self, name, *depends):
    self.__name    = name
    self.__depends = set(depends)

  @property
  def name(self):
    return self.__name

  @property
  def depends(self):
    return self.__depends

  def __str__(self):
    return "Task({}, {})".format(self.__name, self.__depends)

def load_dependency_list(loader, node):
  for item in node.value:
    if item[0].value == "graph_dep":
      return loader.construct_sequence(item[1])
  return []

def load_tasks(loader, node):
  ret = ()
  for item in node.value:
    name = loader.construct_scalar(item[0])
    deplist = load_dependency_list(loader, item[1])
    ret += (Task(name, *deplist),)
  return ret

loader = yaml.SafeLoader(input)
nodes = load_tasks(loader, loader.get_single_node())
for node in nodes:
  print(node)

这段代码的作用是:

  • 通过获取 YAML 文件中单个文档的节点图get_single_node()
  • 在返回的根节点上,执行加载代码

load_tasksload_dependency_list处理 YAML 中的级别。PyYAML 确实提供了将这些函数注册为构造函数的工具,但是只有在 YAML 输入中有标签时才有意义(例如--- !nodes第一行)。如果没有标签,PyYAML 无法自动映射构造函数,因此需要您自己实现构造过程。

在这种情况下,load_dependency_list构造包含在的列表graph_dep或返回空列表,忽略 YAML 节点中的其他内容。load_tasks遍历顶层项目,Task从每个项目构造对象,并将它们连接成一个元组。

我添加了一个__str__方法来Task输出。输出是

Task(a, {'b'})
Task(b, set())

此代码没有任何保护措施,我建议您在访问 YAML 节点的任何地方检查正确的节点类型,以便在结构错误时用户收到良好的错误消息。


推荐阅读