При помощи модуля PyYAML, используя в документе YAML тег !!python/object
, могут быть загружены и созданы экземпляры классов Python.
import yaml # имеющийся класс class Hero(): def __init__(self, name, hp, sp): self.name = name self.hp = hp self.sp = sp def __repr__(self): return(f"{self.__class__.__name__}(name={self.name}, hp={self.hp}, sp={self.sp})") # документ YAML с экземпляром класса `Hero()` doc_yml = """ !!python/object:__main__.Hero name: Welthyr Syxgon hp: 1200 sp: 0 """ # загружаем экземпляр класса `Hero()` new_hero = yaml.load(doc_yml, Loader=yaml.Loader) print(new_hero) # Hero(name=Welthyr Syxgon, hp=1200, sp=0)
Обратите внимание, что возможность создания произвольного объекта Python может быть опасной, если документ YAML получен из ненадежного источника, такого как Интернет. Функция yaml.safe_load()
ограничивает эту возможность простыми объектами Python, такими как целые числа или списки.
Объект python может быть помечен как безопасный и, таким образом, распознается yaml.safe_load()
. Для этого, создаваемый объект унаследуйте от yaml.YAMLObject
и явно установите его свойство класса yaml_loader
на yaml.SafeLoader
.
Например:
import yaml # имеющийся класс class Monster(yaml.YAMLObject): yaml_tag = '!Monster' yaml_loader = yaml.SafeLoader def __init__(self, name, hp, ac, attacks): self.name = name self.hp = hp self.ac = ac self.attacks = attacks def __repr__(self): return (f"{self.__class__.__name__}(name={self.name}," f" hp={self.hp}, ac={self.ac}, attacks={self.attacks})") # документ YAML с экземпляром класса `Monster()` yml_doc = """ --- !Monster name: Cave spider hp: [2,6] # 2d6 ac: 16 attacks: [BITE, HURT] """ # загружаем экземпляр `Monster()` из документ # YAML в безопасном режиме monster1 = yaml.safe_load(yml_doc) # смотрим print(monster1) # Monster(name=Cave spider, hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
Преимущество здесь в том, что над (де)сериализацией класса есть полный контроль. Это означает, что класс не получит случайный исполняемый код по сети и не запустит его. И это же является недостатком, т.е. нужно делать намного больше работы для написания классов.
import yaml class User(): def __init__(self, name, surname): self.name= name self.surname= surname def yaml(self): return yaml.dump(self.__dict__) @staticmethod def load(data): values = yaml.safe_load(data) return User(values["name"], values["surname"]) user = User('spam', 'eggs') serialized_user = user.yaml() print(f"serialized_user: {serialized_user.strip()}") # serialized_user: name: spam # surname: eggs # Network deserialized_user = User.load(serialized_user) print(f"name: {deserialized_user.name}, sname: {deserialized_user.surname}") # name: spam, sname: eggs