<jupyter_start><jupyter_text>Использование yaml Импортируем необходимые библиотеки<jupyter_code>import yaml<jupyter_output><empty_output><jupyter_text>Опишем конфигурацию, описывающую создание персонажа<jupyter_code>hero_yaml = '''
--- !Character
factory:
  !factory assassin
name:
  7NaGiBaToR7
'''<jupyter_output><empty_output><jupyter_text>Используем абстрактную фабрику, использование которой будем конфигурировать<jupyter_code>class HeroFactory:
    @classmethod
    def create_hero(Class, name):
        return Class.Hero(name)
    
    @classmethod
    def create_weapon(Class):
        return Class.Weapon()
    
    @classmethod
    def create_spell(Class):
        return Class.Spell()
class WarriorFactory(HeroFactory):
    class Hero:
        def __init__(self, name):
            self.name = name
            self.weapon = None
            self.armor = None
            self.spell = None

        def add_weapon(self, weapon):
            self.weapon = weapon

        def add_spell(self, spell):
            self.spell = spell

        def hit(self):
            print(f"Warrior {self.name} hits with {self.weapon.hit()}")
            self.weapon.hit()

        def cast(self):
            print(f"Warrior {self.name} casts {self.spell.cast()}")
            self.spell.cast()
              
    class Weapon:
        def hit(self):
            return "Claymore"
        
    class Spell:
        def cast(self):
            return "Power"
        
    
class MageFactory(HeroFactory):
    class Hero:
        def __init__(self, name):
            self.name = name
            self.weapon = None
            self.armor = None
            self.spell = None

        def add_weapon(self, weapon):
            self.weapon = weapon

        def add_spell(self, spell):
            self.spell = spell

        def hit(self):
            print(f"Mage {self.name} hits with {self.weapon.hit()}")
            self.weapon.hit()

        def cast(self):
            print(f"Mage {self.name} casts {self.spell.cast()}")
            self.spell.cast()
            
    class Weapon:
        def hit(self):
            return "Staff"
        
    class Spell:
        def cast(self):
            return "Fireball"
    
        
class AssassinFactory(HeroFactory):
    class Hero:
        def __init__(self, name):
            self.name = name
            self.weapon = None
            self.armor = None
            self.spell = None

        def add_weapon(self, weapon):
            self.weapon = weapon

        def add_spell(self, spell):
            self.spell = spell

        def hit(self):
            print(f"Assassin {self.name} hits with {self.weapon.hit()}")
            self.weapon.hit()

        def cast(self):
            print(f"Assassin {self.name} casts {self.spell.cast()}")
     
    class Weapon:
        def hit(self):
            return "Dagger"
        
    class Spell:
        def cast(self):
            return "Invisibility"<jupyter_output><empty_output><jupyter_text>Опишем конструктор, который сможет обрабатывать узел `!factory`. Он будет возвращать соответствующую фабрику.Конструктор должен принимать 2 аргумента: `loader` и `node`. Объект `loader` — это загрузчик YAML, `node` — узел файла.Поскольку структура YAML-файла древовидная, то при первичном проходе обработчиком всё содержимое файла помещаеться в древовидную структуру, содержащую информацию файла в текстовом виде. `node` является узлом именно такого текстового дерева, а `loader` — загрузчик умеющий обрабатывать `node`. По итогу, ниже следующий конструктор `factory_constructor` будет являться частью `loader` и будет вызываться им по необходимости.Для описанного выше YAML-файла: `loader` — загрузчик, «знакомый» с данным конструктором; `node` — хранит текст `assassin` (информация, хранящаяся за именем пользовательского типом `!factory`) и различную дополнительную информацию.Поскольку `assassin` — простой скаляр, то для его получения (без дополнительной информации) необходимо воспользоваться методом `construct_scalar`. Если бы после `!factory` располагался список, то необходимо было бы воспользоваться методом `construct_sequenc` и т.д.<jupyter_code>def factory_constructor(loader, node):
    data = loader.construct_scalar(node)
    if data == "mage":
        return MageFactory
    elif data == "warrior":
        return WarriorFactory
    else:
        return AssassinFactory<jupyter_output><empty_output><jupyter_text>Опишем класс `Character`, в который будут загружаться данные из yaml. Определим у него метод `create_hero`, позволяющий создать персонажа в соответствии с конфигурацией.<jupyter_code>class Character(yaml.YAMLObject):
    yaml_tag = "!Character"
    
    def create_hero(self):
        hero = self.factory.create_hero(self.name)

        weapon = self.factory.create_weapon()
        spell = self.factory.create_spell()

        hero.add_weapon(weapon)
        hero.add_spell(spell)

        return hero<jupyter_output><empty_output><jupyter_text>Присоединим конструктор и создадим персонажа в соответствии с yaml-конфигурацией.<jupyter_code>loader = yaml.Loader
loader.add_constructor("!factory", factory_constructor)
hero = yaml.load(hero_yaml).create_hero()
hero.hit()
hero.cast()<jupyter_output>Assassin 7NaGiBaToR7 hits with Dagger
Assassin 7NaGiBaToR7 casts Invisibility
{'fraction_non_alphanumeric': 0.0912863070539419, 'fraction_numerical': 0.0013202565069784986, 'mean_word_length': 2.7530077848549186, 'pattern_counts': {'":': 2, '<': 24, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '510697', 'license': 'Apache-2.0', 'n_tokens_mistral': 1798, 'n_tokens_neox': 1916, 'n_words': 408, 'path': 'oop_patterns_python/week_4/pyyaml/Simple_yaml.ipynb', 'repo_name': 'assassinen/coursera_mfti_python', 'size': '5302'}