【问题标题】:PyYAML - Serialize classes (types / class references) that are members of objectsPyYAML - 序列化作为对象成员的类(类型/类引用)
【发布时间】:2018-01-21 18:16:15
【问题描述】:

短版:如何序列化作为对象成员的类(类引用,即不是对象)(参见:示例)?

加长版

我在工作中一直使用这个问题的答案:How can I ignore a member when serializing an object with PyYAML?

所以,我目前的实现是这样的:

class SecretYamlObject(yaml.YAMLObject):
    """Helper class for YAML serialization.
    Source: https://stackoverflow.com/questions/22773612/how-can-i-ignore-a-member-when-serializing-an-object-with-pyyaml """

    def __init__(self, *args, **kwargs):
        self.__setstate__(self, kwargs) #Default behavior, so one could just use setstate
        pass

    hidden_fields = []
    @classmethod
    def to_yaml(cls,dumper,data):
        new_data = copy(data)
        for item in cls.hidden_fields:
            if item in new_data.__dict__:
               del new_data.__dict__[item]
        res = dumper.represent_yaml_object(cls.yaml_tag, new_data, cls, flow_style=cls.yaml_flow_style)
        return res

到目前为止,这对我来说效果很好,因为到目前为止我只需要隐藏记录器:

class EventManager(SecretYamlObject):
    yaml_tag = u"!EventManager"
    hidden_fields = ["logger"]

    def __setstate__(self, kw): # For (de)serialization
        self.logger = logging.getLogger(__name__)
        self.listeners = kw.get("listeners",{})
        #...
        return


    def __init__(self, *args, **kwargs):
        self.__setstate__(kwargs)
        return

但是,当我尝试序列化非平凡对象时会出现一个不同的问题(如果 Q 直接来自对象,这很好,但是从 yaml.YAMLObject 失败并显示“无法腌制 int 对象”)。请参阅此示例

class Q(SecretYamlObject): #works fine if I just use "object"
    pass

class A(SecretYamlObject):
    yaml_tag = u"!Aobj"
    my_q = Q
    def __init__(self, oth_q):
        self.att = "att"
        self.oth_q = oth_q
        pass
    pass

class B(SecretYamlObject):
    yaml_tag = u"!Bobj"
    my_q = Q
    hidden_fields = ["my_q"]
    def __init__(self, oth_q):
        self.att = "att"
        self.oth_q = oth_q
        pass
    pass

class C(SecretYamlObject):
    yaml_tag = u"!Cobj"
    my_q = Q
    hidden_fields = ["my_q"]

    def __init__(self, *args, **kwargs):
        self.__setstate__(kwargs)
        pass

    def __setstate__(self, kw):
        self.att = "att"
        self.my_q = Q
        self.oth_q = kw.get("oth_q",None)
        pass

    pass

a = A(Q)
a2 = yaml.load(yaml.dump(a))

b = B(Q)
b2 = yaml.load(yaml.dump(b))

c = C(my_q=Q)
c2 = yaml.load(yaml.dump(c))
c2.my_q
c2.oth_q

A 和 B 给出“can't pickle int objects”错误,而 C 没有初始化 oth_q(因为没有关于它的信息)。

问题:如何保存有关持有哪个类引用的信息?

(我需要保留类引用才能生成该类型的对象 - 替代方法也可能有效)

【问题讨论】:

    标签: python serialization yaml pyyaml


    【解决方案1】:

    加载转储的 YAML 时,通常不需要保留有关需要实例化哪个类的信息。这就是使用!XObj 存储在文件中的标记信息的用途。

    如果你隐藏对某个类的对象的引用,通过不转储引用它的属性,然后在加载时遇到实例化该对象的问题(因为你不知道它的类),你正在做有事吗。在这种情况下,您应该隐藏被引用对象的内部结构,而不是引用该对象的属性。你可以例如使用!XObj null 转储引用的对象。

    通过隐藏内部,您将拥有适当的标签,在加载时指向正确的类以从中创建对象。您必须根据有限的null 信息来决定您的程序与该对象的内部结构。

    警告:您应该认真重新考虑使用yaml.YAMLObject 的方式。您正在使用记录为不安全的load(),如果您现在和将来的任何时间都不能保证 100% 控制您的 YAML 输入,您可能会丢失驱动器的内容、您的对象的机密性试图隐藏,或者更糟。你应该使用 safe_load() 或者不要使用 PyYAML 之类的库,它默认是不安全的。

    【讨论】:

    • 问题是,我没有持有对象引用,我持有的是类引用。您是否建议我持有该类的空对象,并使用其类型信息即时生成新成员?附言我知道 PyYAML 的不安全性,我正在考虑迁移到 Camel 或其他东西;立即转移到不同的 yaml 库而不是在这里解决问题是否更有意义?
    • 我错过了你实际上只存储引用的部分(也许我更擅长阅读 YAML 而不是阅读其他人的 Python)。我仍然建议尝试使用 YAML 标记工具来解决这个问题,也许可以通过为每个引用的类提供一个额外的生成器类来转储一个对象。如果你还没有这样做,请看看我的 PyYAML 衍生品ruamel.yaml
    • 感谢您的建议。在某些情况下,我能够将类型引用转换为属性(因为它在我的特定类的每个实例中都是相同的)。读了一点后,我也转到了你的图书馆。花了一段时间才弄清楚如何制作代表(仍然需要弄清楚如何反序列化,但这是一个不同的问题......)。但是,我仍然需要在其他对象中保存一些类型引用,而且我不知道如何告诉 ruamel.yaml 如何序列化它。如何注册一个类型的代表而不是该类型的对象?
    • 我想发布一些代码 - 问另一个问题并将其标记为已回答可能是有意义的,对吧?我会在那里做一个更好的例子。
    • @AnatolyMakarevich 是的,一个新问题更好。当事情被标记为 pyyaml/ruamel.yaml 时,我会收到通知,但可能要到下周初才能回答(假设我知道答案),也许其他人可以在此之前回答。
    猜你喜欢
    • 2014-06-07
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    相关资源
    最近更新 更多