2017-02-15 4 views
2

Я пытаюсь справиться с PyYAML, так как мне нравится его читаемость, и хотел бы использовать его в нескольких проектах с открытым исходным кодом. Я работаю как альтернатива JSON.PyYAML и состав, ошибка атрибута

Однако я изо всех сил пытаюсь понять, как построить объекты с композицией. Я открыл этот вопрос: PyYAML - how to deal with compositon и, похоже, работал с точки зрения чтения информации, но не в контексте полной программы.

Вот разобранный пример того, что я пытаюсь YAMLify:

import yaml 
import data 

class DungeonObject(yaml.YAMLObject): 
    yaml_tag = u'!DungeonObject' 
    def __init__(self, x, y, char, name, blocks=False, fighter=None): 
     self.x = x 
     self.y = y 
     self.char = char 
     self.name = name 
     self.blocks = blocks 

     self.fighter = fighter 
     if self.fighter: 
      self.fighter.owner = self 


    def __repr__(self): 
     return "%s(x=%r, y=%r, char=%r, name=%r, blocks=%r fighter=%r)" % (self.__class__.__name__, self.x, self.y, self.char, self.name, self.blocks, self.fighter) 

class Fighter(yaml.YAMLObject): 
    yaml_tag = u'!Fighter' 
    #combat-related properties and methods (monster, player, NPC). 
    def __init__(self, hp, defense, strength): 
     self.hp = hp 
     self.base_defense = defense 
     self.base_strength = strength 
    def __repr__(self): 
     return "%s(hp=%r, defense=%r, strength=%r)" % (self.__class__.__name__, self.hp, self.defense, self.strength) 


monsters = {DungeonObject.name : DungeonObject for DungeonObject in yaml.load_all(data.monsterdata)} 
print (monsters) 

И мой YAML файл:

monsterdata = """ 
--- 
!Fighter &fighter_component 
    hp: 20 
    defense: 0 
    strength: 4 
!DungeonObject 
    x: x 
    y: y 
    char: 'o' 
    name: 'orc' 
    blocks: True 
    fighter: fighter_component 
--- 
!Fighter &fighter_component 
    hp: 9 
    defense: 0 
    strength: 10 
!DungeonObject 
    x: x 
    y: y 
    char: 't' 
    name: 'troll' 
    blocks: True 
    fighter: fighter_component 
""" 

С этим, я получаю сообщение об ошибке: линия 32, in monsters = {DungeonObject.name: DungeonObject для DungeonObject в yaml.load_all (data.monsterdata)} AttributeError: объект 'Fighter' не имеет атрибута 'name'

ответ

1

Каждый документ должен содержать последовательность/список, который состоит из Fighter и DungeonObject. Первый не имеет имени, поэтому вы должны фильтровать на DungeonObjects, которые являются активами типа DungeonObject, а не типа Fighter.

Что немного смущает то, что вы используете переменную DungeonObject, так что попробуйте использовать dungeon_object переменной:

from ruamel import yaml 

monsterdata = """ 
--- 
- !Fighter &fighter_component 
    hp: 20 
    defense: 0 
    strength: 4 
- !DungeonObject 
    x: x 
    y: y 
    char: 'o' 
    name: 'orc' 
    blocks: True 
    fighter: fighter_component 
--- 
- !Fighter &fighter_component 
    hp: 9 
    defense: 0 
    strength: 10 
- !DungeonObject 
    x: x 
    y: y 
    char: 't' 
    name: 'troll' 
    blocks: True 
    fighter: fighter_component 
""" 


class DungeonObject(yaml.YAMLObject): 
    yaml_tag = u'!DungeonObject' 

    def __init__(self, x, y, char, name, blocks=False, fighter=None): 
     self.x = x 
     self.y = y 
     self.char = char 
     self.name = name 
     self.blocks = blocks 

     self.fighter = fighter 
     if self.fighter: 
      self.fighter.owner = self 

    def __repr__(self): 
     return "{}(x={!r}, y={!r}, char={!r}, name={!r}, blocks={!r} fighter={!r})".format(
      self.__class__.__name__, self.x, self.y, self.char, self.name, 
      self.blocks, self.fighter) 


class Fighter(yaml.YAMLObject): 
    yaml_tag = u'!Fighter' 
    # combat-related properties and methods (monster, player, NPC). 

    def __init__(self, hp, defense, strength): 
     self.hp = hp 
     self.base_defense = defense 
     self.base_strength = strength 

    def __repr__(self): 
     return "{}(hp={!r}, defense={!r}, strength={!r})".format(
      self.__class__.__name__, self.hp, self.defense, self.strength) 

monsters = {} 
for doc in yaml.load_all(monsterdata, Loader=yaml.Loader): 
    for dungeon_object in doc: 
     if isinstance(dungeon_object, DungeonObject): 
      monsters[dungeon_object.name] = dungeon_object 

print (monsters) 

Что дает:

{'orc': DungeonObject(x='x', y='y', char='o', name='orc', blocks=True fighter='fighter_component'), 'troll': DungeonObject(x='x', y='y', char='t', name='troll', blocks=True fighter='fighter_component')} 

Я обновил __repr__ к используйте более современный метод .format(). Поскольку я использую ruamel.yaml (который является надмножеством функциональности PyYAML и обратно совместим), я должен явно указать Loader, чтобы подавить предупреждение о том, что load_all небезопасно при использовании загрузчика по умолчанию. (Отказ от ответственности: я являюсь разработчиком этого пакета)

+1

@Retro цитаты вокруг строки исходят из '{! R}' вместо '{}'. Первый вставляет результат из '__repr__' (который для строки представляет собой строку с кавычками), а затем результат из' __str__' (т. Е. Для строки символов без лишних кавычек). – Anthon

+0

Блестящий, что помогает много - все работает сейчас. Конечная проблема, я думаю, состоит в том, что у класса истребителей есть некоторые методы, которые вращаются вокруг использования «владельца», например: 'bonus = sum (equipment.strength_bonus для оборудования в get_all_equipped (self.owner)) return self.base_strength + bonus' Теперь они выбрасывают объект «Истребитель», не имеет атрибута «владелец». Я определил в своем классе Fighter 'if self.fighter: self.fighter.owner', и он работал хорошо раньше, но, похоже, он больше не работает. Есть идеи? – Retro

+1

Если 'self' не имеет владельца, но является классом истребителей, вы, вероятно, не должны использовать' self.owner' в качестве параметра 'get_all_equipped', но используйте' self.fighter.owner'. Но это просто догадка, не видя фактического кода. Удостоверьтесь, что ваш код находится под контролем ревизии и регулярно выполняется, и вы можете сравнить его с тем, что сработало и/или откат. – Anthon

Смежные вопросы