gpt4 book ai didi

python - 从 YAML 文件中以 OOP 方式加载嵌套对象的最佳实践

转载 作者:行者123 更新时间:2023-12-01 07:10:46 25 4
gpt4 key购买 nike

我有一个描述文本游戏内容的 YAML 文件。它包含填充许多嵌套对象所需的数据。这些是场景、 Action 、结果和状态更新。一个场景包含很多 Action ,一个 Action 有很多可能的结果,一个结果可以导致很多状态更新。

我已经为每个实体定义了类,但我不确定从面向对象的角度创建它们的最佳方法。我应该在一大套嵌套的 for 循环中创建所有内容(例如在游戏的初始化中),还是应该将数据传递到较低级别的类并逐个解析它?

使用前一种方法,我觉得对象的构造及其关系会更容易理解。然而,使用后者,我觉得编写单元测试会更容易。

我使用安全的 PyYAML 加载器加载它,因此标记为特定的 python 对象是不可行的。

请给出支持和反对每种方法或其他替代方案的理由。

最佳答案

虽然“标记为特定的Python对象是不可行的”,但它可以使用明确的标签来标记它们,即使在安全装载机的限制。这不会导致加载不安全你的 YAML 文件,除非从这些标签创建的对象不安全启动时的副作用。

使用标签还允许您转储(和加载)您的数据结构即使您在发展的某个阶段有过内存从多个对象到一个特定的其他对象的引用或当您有对象(间接)引用其自身时。这些将使用 anchor 和别名进行转储,并且解析起来并不简单这些东西你自己在转储时不使用标签,就像你一样需要跟踪已经转储的对象。

可以轻松编写个人单元测试。通常你唯一要做的就是必须确保您可以创建一个没有 Action 的场景,然后您可以测试场景中不涉及 Action 等的所有内容。

PyYAML 可以解析所有 YAML 1.1 规范,但无法加载所有该规范。此外,YAML 1.2 已经发布十年了。两者都可能不是您的程序的限制,但我确实建议使用ruamel.yaml,它没有这些问题(免责声明:我是该包的作者)。如何转储/加载类与 ruamel.yaml 一起描述 here

import sys
from pathlib import Path
import ruamel.yaml

data_file = Path('data.yaml')

yaml = ruamel.yaml.YAML(typ='safe')

@yaml.register_class
class Scene:
def __init__(self, sc_parm, actions):
self.sc_parm = sc_parm
self.actions = actions

@yaml.register_class
class Action:
def __init__(self, ac_parm1, ac_parm2):
self.ac_parm1 = ac_parm2
self.ac_parm2 = ac_parm2


shared_action = Action('south', 1)

data = [
Scene('sc1_parm_val', []),
Scene('sc2_parm_val', [Action('north', 3), shared_action]),
Scene('sc3_parm_val', [shared_action, Action('west', 2)]),
]

yaml.dump(data, data_file)

print(data_file.read_text(), end='')
print('=+'*30)
d2 = yaml.load(data_file)

for d in d2:
print(d.sc_parm, d.actions)

给出:

- !Scene
actions: []
sc_parm: sc1_parm_val
- !Scene
actions:
- !Action {ac_parm1: 3, ac_parm2: 3}
- &id001 !Action {ac_parm1: 1, ac_parm2: 1}
sc_parm: sc2_parm_val
- !Scene
actions:
- *id001
- !Action {ac_parm1: 2, ac_parm2: 2}
sc_parm: sc3_parm_val
=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sc1_parm_val []
sc2_parm_val [<__main__.Action object at 0x7f009be56f28>, <__main__.Action object at 0x7f009be56fd0>]
sc3_parm_val [<__main__.Action object at 0x7f009be56fd0>, <__main__.Action object at 0x7f009be56e48>]

Action 对象的 ID 中可以看出,第二个操作第二个场景的第一个 Action 再次与最后一个场景的第一个 Action 相同场景(原始shared_action)

请确保,如果您决定制作自己的 from_yaml (而不是依赖于您通过注册获得的默认值),例如为了限制转储的属性,您使用以下两步过程创建对象,因为这是允许递归数据所必需的树中的结构。您可能不会立即需要这个,但它是相当容易做到,例如描述 here为了往返加载程序(可以保留注释、引用的安全加载程序)加载然后转储 YAML 时的样式、别名等)。

关于python - 从 YAML 文件中以 OOP 方式加载嵌套对象的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58228199/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com