Рекомендации по загрузке вложенных объектов в стиле ООП из файла YAML - PullRequest
1 голос
/ 04 октября 2019

У меня есть файл YAML, который описывает содержание текстовой игры. Он содержит данные, необходимые для заполнения многих вложенных объектов. Это сцены, действия, результаты и обновления состояния. Сцена содержит много действий, у действия есть много возможных результатов, и результат может привести к обновлению многих состояний.

Я определил классы для каждой из этих сущностей, но я не уверен в наилучшем способе их создания. с объектно-ориентированной точки зрения. Должен ли я создавать все в одном большом вложенном наборе циклов for (например, в начале игры) или я должен передавать данные в классы более низкого уровня и анализировать их по частям?

При первом подходе я чувствую, что построение объектов и их взаимосвязей будет легче понять. Однако, с последним, я чувствую, что было бы легче написать модульные тесты.

Я загружаю его, используя безопасный загрузчик PyYAML, поэтому пометить как отдельные объекты Python невозможно.

ПожалуйстаУкажите причины и против каждого подхода или других альтернатив.

1 Ответ

1 голос
/ 04 октября 2019

Несмотря на то, что «маркировка как отдельных объектов python невозможна», возможно выполнимо пометить их явными тегами даже в рамках ограничений безопасного загрузчика. Это не делает небезопасной загрузку YAML-файла, если только объекты, созданные из этих тегов, не имеют небезопасных побочных эффектов при инициации.

Использование тегов также позволяет вам выгружать (и загружать) структуру данных, которую выхранится в памяти, даже если в какой-то момент в вашей разработке есть ссылки от более чем одного объекта на один конкретный другой объект или когда у вас есть объекты с (косвенными) ссылками на себя. Они будут сбрасываться с помощью якорей и псевдонимов, и нетрудно разрешить эти вещи самостоятельно при сбросе без использования тегов, так как вам нужно будет отслеживать уже сброшенные объекты.

Unittest для отдельных пользователей может быть легконаписано. Обычно единственное, что вы должны убедиться, это то, что вы можете создать сцену без действий, затем вы можете протестировать все для сцены, которая не включает в себя действия и т. Д.

PyYAML может анализировать все YAML 1.1спецификация, но не может загрузить всю эту спецификацию. Кроме того, YAML 1.2 существует уже десять лет. Ни один из них не может быть ограничением для вашей программы, но я рекомендую использовать ruamel.yaml, у которого нет этих проблем (отказ от ответственности: я являюсь автором этого пакета). Как выгружать / загружать классы с ruamel.yaml описано здесь

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 объектов, второе действие второй сцены снова совпадает с первым действием последней сцены (оригинал shared_action)

Убедитесь, что если вы решите сделать свою собственную from_yaml(вместо того, чтобы полагаться на значение по умолчанию, которое вы получаете при регистрации), например, чтобы ограничить сбрасываемые атрибуты, вы используете двухэтапный процесс создания ваших объектов, поскольку это необходимо для обеспечения рекурсивных структур данных в вашем дереве. Возможно, вам это не понадобится сразу, но это довольно просто сделать и, например, описать здесь для загрузчика туда и обратно (безопасный загрузчик, который может сохранять комментарии, стили цитирования, псевдонимы и т. Д. При загрузке затемдемпинг ЯМЛ).

...