我只想使用YAML存储表示配置的专用Python对象,并在需要时将其加载回去。
我的应用程序允许定义由嵌套Python对象构成的场景。
根对象(命名为场景)包含一些属性和对象列表(命名为Level1Type1、Level1Type2,…),
这些Level1对象中的每一个都由一些属性和对象列表组成(将它们命名为Level2Type1、Level2Type2等)。
总而言之,它是一棵树。每个叶子都有属性和一个本身就是叶子的其他对象列表。
此外,只需将对象属性的一部分保存在文件中(动态属性对配置文件不感兴趣)。
我决定明确定义保存哪些属性
阅读谷歌检索的主题为“Python用yaml序列化对象”的文档,给了我一些提示,但让我感到困惑
真正需要什么。
其中大部分都是由安顿提供的(非常感谢他)。它主要解释了我为什么使用ruamel.yaml。
遗憾的是:由于缺乏关于Python任意对象序列化的解释和文档,我在这个问题上花了太多时间
我注意到,当使用YAML文件重新加载场景时,使用对象构造函数创建的场景中的列表对象变成了CommentedSeq
对象。
我还想知道我在许多例子中看到的__repr__
定义的目标。它是否由序列化机制使用
下面是验证我的应用程序需求的代码
import ruamel.yaml
from io import StringIO
yaml = ruamel.yaml.YAML()
class Base:
"""
Base class for every class in the tree.
"""
# class variables can be necessary
cst_value = "common"
def __init__(self, elt_name=None, comment=None):
self.elt_name = elt_name
self.comment = comment
def treatment(self):
raise NotImplementedError('Base class should not be implemented')
@yaml.register_class
class Scenario(Base):
yaml_tag = u'!Scenario'
def __init__(self, elt_name=None, comment=None, level1_objs=None):
super().__init__(elt_name, comment)
# List of level1 objects: to be saved in yaml file.
self.level1_objs = [] if level1_objs is None else level1_objs
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'level1_objs': node.level1_objs
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
level1_objs = m['level1_objs'] if 'level1_objs' in m else None
return cls(elt_name, comment, level1_objs)
def treatment(self):
pass
class BunchOfData:
def __init__(self):
self.data_frame = None
self.data1 = None
self.data2 = None
self.data3 = None
@yaml.register_class
class Level1Type1(Base):
yaml_tag = u'!Level1Type1'
def __init__(self, elt_name=None, comment=None, level2_objs=None, l1_t1_attr1=None):
super().__init__(elt_name, comment)
# List of level2 objects: to be saved in yaml file.
self.level2_objs = [] if level2_objs is None else level2_objs
# Attribute: to be saved in yaml file.
self.l1_t1_attr1 = l1_t1_attr1
# Dynamic attribute: Not to be saved in yaml file
self.dyn_data = BunchOfData()
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'l1_t1_attr1': node.l1_t1_attr1,
'level2_objs': node.level2_objs
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
level2_objs = m['level2_objs'] if 'level2_objs' in m else None
l1_t1_attr1 = m['l1_t1_attr1'] if 'l1_t1_attr1' in m else None
return cls(elt_name, comment, level2_objs, l1_t1_attr1)
def treatment(self):
pass
@yaml.register_class
class Level1Type2(Base):
yaml_tag = u'!Level1Type2'
def __init__(self, elt_name=None, comment=None, level2_objs=None, l1_t2_attr1=None, l1_t2_attr2=None):
super().__init__(elt_name, comment)
# List of level2 objects: to be saved in yaml file.
self.level2_objs = [] if level2_objs is None else level2_objs
# Attribute: to be saved in yaml file.
self.l1_t2_attr1 = l1_t2_attr1
self.l1_t2_attr2 = l1_t2_attr2
# Dynamic attribute: Not to be saved in yaml file
self.dyn_data = BunchOfData()
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'level2_objs': node.level2_objs,
'l1_t2_attr1': node.l1_t2_attr1,
'l1_t2_attr2': node.l1_t2_attr2
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
level2_objs = m['level2_objs'] if 'level2_objs' in m else None
l1_t2_attr1 = m['l1_t2_attr1'] if 'l1_t2_attr1' in m else None
l1_t2_attr2 = m['l1_t2_attr2'] if 'l1_t2_attr2' in m else None
return cls(elt_name, comment, level2_objs, l1_t2_attr1, l1_t2_attr2)
def treatment(self):
pass
@yaml.register_class
class Level2Type1(Base):
yaml_tag = u'!Level2Type1'
def __init__(self, elt_name=None, comment=None, l2_t1_attr1=None):
super().__init__(elt_name, comment)
# Attribute: to be saved in yaml file.
self.l2_t1_attr1 = l2_t1_attr1
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'l2_t1_attr1': node.l2_t1_attr1
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
l2_t1_attr1 = m['l2_t1_attr1'] if 'l2_t1_attr1' in m else None
return cls(elt_name, comment, l2_t1_attr1)
def treatment(self):
pass
@yaml.register_class
class Level2Type2(Base):
yaml_tag = u'!Level2Type2'
def __init__(self, elt_name=None, comment=None, l2_t2_attr1=None, l2_t2_attr2=None):
super().__init__(elt_name, comment)
# Attribute: to be saved in yaml file.
self.l2_t2_attr1 = l2_t2_attr1
self.l2_t2_attr2 = l2_t2_attr2
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'l2_t2_attr1': node.l2_t2_attr1,
'l2_t2_attr2': node.l2_t2_attr2
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
l2_t2_attr1 = m['l2_t2_attr1'] if 'l2_t2_attr1' in m else None
l2_t2_attr2 = m['l2_t2_attr2'] if 'l2_t2_attr2' in m else None
return cls(elt_name, comment, l2_t2_attr1, l2_t2_attr2)
def treatment(self):
pass
@yaml.register_class
class Level2Type3(Base):
yaml_tag = u'!Level2Type3'
def __init__(self, elt_name=None, comment=None, l2_t3_attr1=None, l2_t3_attr2=None, l2_t3_attr3=None):
super().__init__(elt_name, comment)
# Attribute: to be saved in yaml file.
self.l2_t3_attr1 = l2_t3_attr1
self.l2_t3_attr2 = l2_t3_attr2
self.l2_t3_attr3 = l2_t3_attr3
@classmethod
def to_yaml(cls, representer, node):
dict_representation = {
'elt_name': node.elt_name,
'comment': node.comment,
'l2_t3_attr1': node.l2_t3_attr1,
'l2_t3_attr2': node.l2_t3_attr2,
'l2_t3_attr3': node.l2_t3_attr3
}
return representer.represent_mapping(cls.yaml_tag, dict_representation)
@classmethod
def from_yaml(cls, constructor, node):
m = {}
for m in constructor.construct_yaml_map(node):
pass
elt_name = m['elt_name'] if 'elt_name' in m else None
comment = m['comment'] if 'comment' in m else None
l2_t3_attr1 = m['l2_t3_attr1'] if 'l2_t3_attr1' in m else None
l2_t3_attr2 = m['l2_t3_attr2'] if 'l2_t3_attr2' in m else None
l2_t3_attr3 = m['l2_t3_attr3'] if 'l2_t3_attr3' in m else None
return cls(elt_name, comment, l2_t3_attr1, l2_t3_attr2, l2_t3_attr3)
def treatment(self):
pass
# Make this run.
test = Scenario("my_scenario", "what a scenario may look like after yaml dump",
[Level1Type1("l1_t1_object", "I am a Level1 Type1 object", [
Level2Type1("l2_t1_object", "I am a Level2 Type1 object", 11211),
Level2Type2("l2_t2_object", "I am a Level2 Type2 object", 11221, 11222),
Level2Type3("l2_t3_object", "I am a Level2 Type3 object", 11231, 11232, 11233),
], 111),
Level1Type2("l1_t2_object", "I am a Level1 Type2 object", [
Level2Type2("l2_t2_object", "I am a Level2 Type2 object", 12221, 12222),
Level2Type1("l2_t1_object", "I am a Level2 Type1 object", 12211),
Level2Type3("l2_t3_object", "I am a Level2 Type3 object", 12231, 12232, 12233),
], 121, 122)
]
)
# serialize
dump_buf = StringIO()
yaml.dump(test, dump_buf)
test_serialized = dump_buf.getvalue()
print(test_serialized)
# deserialize
test_is_back = yaml.load(test_serialized)
print(test_is_back)
生成的yaml文件如下所示:
!Scenario
elt_name: my_scenario
comment: what a scenario may look like after yaml dump
level1_objs:
- !Level1Type1
elt_name: l1_t1_object
comment: I am a Level1 Type1 object
l1_t1_attr1: 111
level2_objs:
- !Level2Type1
elt_name: l2_t1_object
comment: I am a Level2 Type1 object
l2_t1_attr1: 11211
- !Level2Type2
elt_name: l2_t2_object
....
这不是一个代码审查站点,因此我将仅限于真实的和IMO隐含的问题:
至于真正的问题。否,序列化不使用
__repr__
过程,只是为了确保您可以“打印”实例,并获得一些人力资源 可解释的表示,而不是您将得到的<__module__.Type. object at 0xaddress>
至于隐含的问题:你得到的是
CommentedSeq
而不是“正常” 列表,因为您使用默认的往返装载机/卸载机该加载程序/转储程序需要能够附加注释(以及锚/别名和 未注册标签的标签信息),它可以这样做}上执行此操作
CommentedSeq
实例,因为它无法在内置{CommentedSeq
在大多数方面表现为list
,但如果这是一个问题 以这样或那样的方式,或者如果您不需要任何往返功能 (就像您的情况一样),您应该只使用:(这将为您提供更快但不完全兼容的基于C的YAML 1.1装载机/卸载机)
或使用:
这使加载程序/转储程序无需完全往返所需的“开销”,因此加载回
list
而不是CommentedSeq
即使使用 往返加载器,但这不是微不足道的。但是如果您使用的是YAML文档 对于转储和加载,不应在注释中存储任何信息 只需使用安全的自卸车/装载机
相关问题 更多 >
编程相关推荐