PyYAML - 序列化作为对象成员的 classes(类型/class 引用)
PyYAML - Serialize classes (types / class references) that are members of objects
简短版本:如何序列化作为对象成员的 class(class 引用,即不是对象)(参见: 例子)?
长版:
我一直在工作中使用这个问题的答案:How can I ignore a member when serializing an object with PyYAML?
所以,我目前的实现是这样的:
class SecretYamlObject(yaml.YAMLObject):
"""Helper class for YAML serialization.
Source: """
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 它失败并显示 "can't pickle int objects")。请参阅此 示例 :
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(因为没有关于它的信息)。
问题:如何保存关于class引用的信息?
(我需要保留 class 引用才能创建该类型的对象 - 替代方法也可能有效)
加载转储的 YAML 时,通常不需要保留有关 class 需要实例化的信息。这就是用 !XObj
存储在文件中的标签信息的用途。
如果您隐藏了对某个 class 对象的引用,方法是不转储引用它的属性,然后 运行 会遇到实例化该对象的问题(因为您不加载时知道它的class),你做错了什么。在那种情况下,您应该隐藏引用对象的内部结构,而不是引用对象的属性。你可以例如使用 !XObj null
.
转储引用对象
通过隐藏内部结构,您将拥有适当的标签,在加载时指向右侧 class 以创建对象。您必须根据有限的 null
信息来决定您的程序具有该对象的内部结构。
警告:您应该认真地重新考虑使用 yaml.YAMLObject
的方式。您正在使用记录为不安全的 load()
,如果您现在和将来任何时候都不能保证 100% 控制您的 YAML 输入,您可能会丢失驱动器的内容、你试图隐藏的对象,或者更糟。您应该使用 safe_load()
或放弃使用像 PyYAML 这样的默认不安全的库。
简短版本:如何序列化作为对象成员的 class(class 引用,即不是对象)(参见: 例子)?
长版:
我一直在工作中使用这个问题的答案:How can I ignore a member when serializing an object with PyYAML?
所以,我目前的实现是这样的:
class SecretYamlObject(yaml.YAMLObject):
"""Helper class for YAML serialization.
Source: """
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 它失败并显示 "can't pickle int objects")。请参阅此 示例 :
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(因为没有关于它的信息)。
问题:如何保存关于class引用的信息?
(我需要保留 class 引用才能创建该类型的对象 - 替代方法也可能有效)
加载转储的 YAML 时,通常不需要保留有关 class 需要实例化的信息。这就是用 !XObj
存储在文件中的标签信息的用途。
如果您隐藏了对某个 class 对象的引用,方法是不转储引用它的属性,然后 运行 会遇到实例化该对象的问题(因为您不加载时知道它的class),你做错了什么。在那种情况下,您应该隐藏引用对象的内部结构,而不是引用对象的属性。你可以例如使用 !XObj null
.
通过隐藏内部结构,您将拥有适当的标签,在加载时指向右侧 class 以创建对象。您必须根据有限的 null
信息来决定您的程序具有该对象的内部结构。
警告:您应该认真地重新考虑使用 yaml.YAMLObject
的方式。您正在使用记录为不安全的 load()
,如果您现在和将来任何时候都不能保证 100% 控制您的 YAML 输入,您可能会丢失驱动器的内容、你试图隐藏的对象,或者更糟。您应该使用 safe_load()
或放弃使用像 PyYAML 这样的默认不安全的库。