Сбой десериализации словаря с пользовательскими ключами класса в PyYAML

#python #pyyaml

#python #pyyaml

Вопрос:

Я пытался использовать PyYAML для сериализации словаря, который использует экземпляры SampleClass в качестве ключей. Он сериализуется нормально, но когда я пытаюсь загрузить его с помощью yaml.load() , возникает исключение:

Ошибка атрибута: объект ‘SampleClass’ не имеет атрибута ‘name’

Как это можно исправить? SampleClass Выглядит так:

 import uuid

class SampleClass:

    def __init__(self, name = "<NO NAME>"):
        self.objects = []
        self.name = name
        self.id = uuid.uuid1()

    def __eq__(self, other):
        if isinstance(other, SampleClass):
            return self.name == other.name and 
                self.objects == other.objects and 
                self.id == other.id
        else:
            return False

    def __hash__(self):
        return hash((str(self.name), str(self.id)))
  

Ответ №1:

PyYAML немного устарел, он поддерживает только YAML 1.1, который был заменен YAML 1.2 еще в 2009 году. Также обратите внимание, что, хотя PyYAML может анализировать сложные ключи в сопоставлениях YAML (например, ключи, которые являются последовательностями или самими отображениями), ключи, которые действительны в YAML, при их создании в Python происходит сбой, фактически не имея возможности загрузить их.

С ruamel.yaml (отказ от ответственности: я являюсь автором этого пакета) вы можете просто сделать:

 import sys
import uuid
import ruamel.yaml
from ruamel.yaml.compat import StringIO

class SampleClass:

    def __init__(self, name = "<NO NAME>"):
        self.objects = []
        self.name = name
        self.id = uuid.uuid1()

    def __eq__(self, other):
        if isinstance(other, SampleClass):
            return self.name == other.name and 
                self.objects == other.objects and 
                self.id == other.id
        else:
            return False

    def __hash__(self):
        return hash((str(self.name), str(self.id)))

    def __repr__(self):
        return "SampleClass({})".format(self. name)

data = {SampleClass("abc"): 1, SampleClass("xyz"): 42}

yaml = ruamel.yaml.YAML(typ="unsafe")
buf = StringIO()
yaml.dump(data, buf)
x = yaml.load(buf.getvalue())
print(x)
  

что дает:

 {SampleClass(abc): 1, SampleClass(xyz): 42}
  

Однако я рекомендую предоставить процедуры to_yaml и from_yaml
для SampleClass и регистрации класса
(документ). Это
позволяет устранить небезопасную загрузку (которая, кстати,
по умолчанию для PyYAML).