【发布时间】:2021-04-25 11:20:36
【问题描述】:
我想要一些关于将自定义类对象转储到 YAML 文件的帮助。我的班级的代表如下所示:
from classes.enum_classes import Priority, Deadline
class Test(yaml.YAMLObject):
yaml_tag = u'!RandomTestClass'
def __init__(self):
self._name = ""
self._request_id = ""
self._order = None
self._priority = Priority.medium
self._deadline = Deadline.label_c
最后两个参数是不同类的对象,都是Enum派生类。我正在尝试将 Test 类的对象的内容转储到 YAML 输出文件中。我的 Test 类的 __repr__ 方法看起来是这样的:
def __repr__(self):
return "(name=%r, request=%r, order=%r, priority=%r, deadline=%r)" % \
((str(self.name) + str(self.order)), self.request_id, self.order,
self.priority.name, self._deadline.name)
处理 PyYAML 文档的构造函数和表示器部分以及那里提供的示例(特别是考虑到作者对某些类变量使用列表),我希望我的 YAML 文件在 @ 中显示实际的显示标签987654328@ 方法,而不是变量名本身。这是我目前看到的:
--- !ContainerClass
_requests: !!python/object/apply:collections.defaultdict
args:
- !!python/name:builtins.list ''
dictitems:
'108':
- !RandomTestClass
_deadline: &id002 !Deadline '3'
_name: '108.1'
_order: '1'
_priority: &id006 !Priority '1'
_request_id: '108'
- !RandomTestClass
_deadline: &id002 !Deadline '3'
_name: '108.2'
_order: '2'
_priority: &id006 !Priority '1'
_request_id: '108'
_name: TestContainer
我希望它看起来像这样:
---
Requests:
- name: '108.1'
- requestID: '108'
- priority: '1'
- order: '1'
- deadline: '3'
- name: '108.2' <for the second entry in the list and so on>
Name: ContainerClass
__repr__ 方法或其他任何东西都不会导致我想要的输出。所以有两个问题我很想得到一些帮助。
-
如何获得理智、可读的表示?我猜我将不得不编写一个自定义表示器,所以如果有人可以指导我一些指针,因为我无法以任何方式找到太多信息。
-
摆脱那些指针,或者我们想要在优先级和截止日期旁边调用它们的任何东西。 Priority 和 Deadline 是我上面的
__init___方法中引用的两个类,它们是Enum子类。由于它们已经是子类,因此任何将它们子类化为yaml.YAMLObject的尝试都会导致 mixins 出现错误。为了解决这个问题,一些帖子建议我这样做:
class Priority(Enum):
low = 0
medium = 1
high = 2
class Deadline(Enum):
label_a = 0
label_b = 1
label_c = 2
label_d = 3
def priority_enum_representer(dumper, data):
return dumper.represent_scalar('!Priority', str(data.value))
def deadline_enum_representer(dumper, data):
return dumper.represent_scalar('!Deadline', str(data.value))
yaml.add_representer(Deadline, deadline_enum_representer)
yaml.add_representer(Priority, priority_enum_representer)
非常感谢任何有关解决这两个问题的信息/指针。为这篇长文道歉,但我了解到,更多信息通常会带来更准确的帮助。
更新:
我的 YAML 文件是根据这些 RandomTestClass 对象的列表编写的,这些对象存储在 defaultdict(list) 中的 ContainerClass 中。
class ContainerClass(yaml.YAMLObject):
yaml_tag = u'ContainerClass'
def __init__(self):
self._name = ""
self._requests = defaultdict(list)
def __repr__(self):
return "(Name=%r, Requests=%r)" % \
(self._name, str(self._requests))
@property
def requests(self):
return self._requests
@requests.setter
def requests(self, new_req, value=None):
if type(new_req) is dict:
self._requests = new_req
else:
try:
self._requests[new_req].append(value)
except AttributeError:
# This means the key exists, but the value is a non-list
# entity. Change existing value to list, append new value
# and reassign to the same key
list_with_values \
= [self._requests[new_req], value]
self._requests[new_req] = list_with_values
ContainerClass 包含 Test 对象的实例。在另一个类中,这是我包含__main__ 的代码的入口点,我创建了Test 对象的多个实例,然后将它们存储在一个```ContainerClass`` 对象中并转储到YAML 文件中。
# requisite imports here, based on
# how the files are stored
from classes.Test import Test
from classes.ContainerClass import ContainerClass
class RunTestClass:
if __name__ == '__main__':
yaml_container = ContainerClass()
test_object_a = Test()
test_object_a._name = '108.1'
test_object_a._order = 1
test_object_a._request_id = '108'
yaml_container._name = "TestContainer"
yaml_container._requests[test_object_a._request_id] = test_object_a
test_object_b = Test()
test_object_b._name = '108.2'
test_object_b._order = 2
test_object_b._request_id = '108'
yaml_container._name = "TestContainer"
yaml_container._requests[test_object_b._request_id] = test_object_b
with open(output_file, mode='w+') as outfile:
for test_class_object in yaml_container._requests:
yaml.dump(test_class_object, outfile, default_flow_style=False,
explicit_start=True, explicit_end=True)
更新:
将单个合并文件添加到问题中,可执行以复制问题。
import yaml
from enum import Enum
from collections import defaultdict
class Priority(Enum):
low = 0
medium = 1
high = 2
class Deadline(Enum):
label_a = 0
label_b = 1
label_c = 2
label_d = 3
def priority_enum_representer(dumper, data):
return dumper.represent_scalar('!Priority', str(data.value))
def deadline_enum_representer(dumper, data):
return dumper.represent_scalar('!Deadline', str(data.value))
yaml.add_representer(Deadline, deadline_enum_representer)
yaml.add_representer(Priority, priority_enum_representer)
class Test(yaml.YAMLObject):
yaml_tag = u'!RandomTestClass'
def __init__(self):
self._name = ""
self._request_id = ""
self._order = None
self._priority = Priority.medium
self._deadline = Deadline.label_c
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def request_id(self):
return self._request_id
@request_id.setter
def request_id(self, r_id):
self._request_id = r_id
@property
def order(self):
return self._order
@order.setter
def order(self, order):
self._order = order
@property
def priority(self):
return self._priority
@priority.setter
def priority(self, priority):
self._priority = priority
@property
def deadline(self):
return self._deadline
@deadline.setter
def deadline(self, deadline):
self._deadline = deadline
def __str__(self):
return self.name + ", " + self._request_id + ", " + str(self.order) + ", " \
+ str(self.priority) + ", " + str(self.deadline)
class ContainerClass(yaml.YAMLObject):
yaml_tag = u'ContainerClass'
def __init__(self):
self._name = ""
self._requests = defaultdict(list)
def __repr__(self):
return "(Name=%r, Requests=%r)" % \
(self._name, str(self._requests))
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def requests(self):
return self._requests
def set_requests(self, new_req, value=None):
if type(new_req) is dict:
self._requests = new_req
else:
try:
self._requests[new_req].append(value)
except AttributeError:
# This means the key exists, but the value is a non-list
# entity. Change existing value to list, append new value
# and reassign to the same key
print("Encountered a single value, converting to a list and appending new value")
list_with_values \
= [self._requests[new_req], value]
self._requests[new_req] = list_with_values
yaml_container = ContainerClass()
yaml_container.name = "TestContainer"
test_object_a = Test()
test_object_a._name = '108.1'
test_object_a._order = 1
test_object_a._request_id = '108'
yaml_container.set_requests(test_object_a.request_id, test_object_a)
test_object_b = Test()
test_object_b._name = '108.2'
test_object_b._order = 2
test_object_b._request_id = '108'
yaml_container.set_requests(test_object_b.request_id, test_object_b)
with open('test.yaml', mode='w+') as outfile:
yaml.dump(yaml_container, outfile, default_flow_style=False,
explicit_start=True, explicit_end=True)
【问题讨论】:
-
请显示实际代码以重现您的输出。给定的 YAML 意味着您不只是序列化
Test的实例,而是包含它的值,并且不清楚您希望这些周围的值如何以及为什么消失。在任何情况下,__repr__不用于使用 YAML 序列化您的类,文档仅使用它来向您展示加载示例中值的漂亮呈现。 -
@flyx:这是我的实际代码,大部分情况下只是隐藏了变量名称。我尝试遵循 PyYAML 文档和示例,但我想我不确定序列化实例是什么意思。我只是使用
yaml.dump()来做到这一点。 -
@flyx:我生成的输出 YAML 文件正在使用我无法修改的其他人的代码,因此需要以我展示的方式复制标签等。如果不是,我最终会破坏他们的代码。
-
请发布minimal, reproducible example,不要引用
test_class_object等未定义变量。您显示的代码没有解释!!python/object/apply:collections.defaultdict的来源,并且通常似乎不是用于生成输出的代码,因为它定义了显式的开始/结束,而您的 YAML 两者都不包含。 -
@flyx:希望这就足够了。可能需要根据环境进行一些小的更改,但这是我可以在不泄露任何实际代码的情况下复制的内容。我在 TestClass 中省略了 getter/setter,我想这不是必需的,因为您可以按原样获取、设置值。
标签: python-3.x yaml pyyaml