从yamlfi加载嵌套对象的OOP风格的最佳实践

2024-04-18 02:35:34 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我已经为这些实体中的每一个定义了类,但是我不确定从面向对象的角度来创建它们的最佳方法。我应该在一个大的for循环嵌套集合中创建所有的东西(比如在游戏的init中),还是应该将数据传递给较低级别的类并逐个解析它?你知道吗

使用前一种方法,我觉得对象的构造和它们之间的关系更容易理解。但是,使用后者,我觉得编写单元测试会更容易。你知道吗

我使用一个安全的PyYAML加载程序加载它,所以标记为特定的python对象是不可行的。你知道吗

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


Tags: 文件数据对象方法文本实体游戏yaml
1条回答
网友
1楼 · 发布于 2024-04-18 02:35:34

尽管“标记为特定的python对象是不可行的”,但是 用显式标记对它们进行标记是可行的,即使在 安全装载机的限制。这并不意味着装载不安全 您的YAML文件,除非从这些标记创建的对象具有不安全的 启动时的副作用。你知道吗

使用标记还允许您转储(和加载)所需的数据结构 即使你在发展过程中的某个阶段 从多个对象到另一个特定对象的引用或 当您有对象(间接)引用自身时。这些将 使用锚点和别名转储,这是非常重要的解决方法 这些东西你自己当倾倒不使用标签,因为你会 需要跟踪已经倾倒的物体。你知道吗

为个人编写单元测试很容易。通常你 必须确保你可以创建一个没有动作的场景,然后你可以 测试所有不涉及动作的场景等

PyYAML可以解析所有yaml1.1规范,但不能加载 所有这些规格。此外,yaml1.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可以看到,第二个动作 第二幕的第一幕和最后一幕的第一幕一样 场景(原始的shared_action

如果你决定做你自己的from_yaml(而不是 依赖于您通过注册获得的默认值),例如,为了限制 被转储的属性,使用 创建对象,因为这是允许递归数据所必需的 树上的结构。你可能不需要这个,但它是 很容易做,例如描述 here对于 往返加载器(一个安全的加载器,可以保留评论,引用 样式、别名等。你知道吗

相关问题 更多 >