将Python对象转储为YAML文件

10

我遇到了将下面显示的Python对象转储到yaml文件的问题。 我想知道是否有任何特定的对象类型/格式。

class CameraBrand():

  def __init__(self, name, url):
    self.rank = ""
    self.name = name
    self.url = url
    self.models = []

  def __str__(self):
      return self.name + ": " + self.url

这个对象包含对象列表,并且我尝试使用 yaml.safe_dump

  yaml.safe_dump(brandObject, file)

但是我遇到了这个错误:
yaml.representer.RepresenterError: cannot represent an object: Canon: /cameras/canon/

这个对象有问题吗?

3个回答

17

这是一篇旧帖,但为了补充Jayanth Koushik的答案:

我不确定没有实现__repr__是罪魁祸首。我尝试实现它并仍然会引发错误,所以解决方案可能不正确:

import yaml

class CameraBrand():

  def __init__(self, name, url):
    self.rank = ""
    self.name = name
    self.url = url
    self.models = []

  def __str__(self):
      return repr(self)

  def __repr__(self):
      return self.name + ": " + self.url


brand_object = CameraBrand('foo', 'http://foo.com')
print(yaml.safe_dump(brand_object))

仍然引发了yaml.representer.RepresenterError: cannot represent an object: foo: http://foo.com

实际上,答案可以在PyYaml文档中找到:“safe_dump仅生成标准的YAML标记,并且无法表示任意Python对象。” 因此,您只需要使用dump而不是safe_dump

>>> print(yaml.dump(brand_object))

!!python/object:__main__.CameraBrand
models: []
name: foo
rank: ''
url: http://foo.com
然而,一旦你这样做了,你会发现使用不安全且不建议的yaml.load可以加载你的对象,但使用高度推荐的safe_load则有点棘手。对此,你需要深入研究YAMLObject和其他PyYaml类,这有点困难。
  • 或者,您可以使用yamlable库,它可以自动化一些PyYaml技巧。我编写它是为了解决我们生产代码中类似的情况,以使更容易控制对象到yaml绑定的过程。在您的情况下,以下操作将解决问题:
    import yaml
    from yamlable import YamlAble, yaml_info
    
    @yaml_info(yaml_tag_ns='myyaml')
    class CameraBrand(YamlAble):
    
        def __init__(self, name, url):
            self.rank = ""
            self.name = name
            self.url = url
            self.models = []
    
        def __str__(self):
            return self.name + ": " + self.url
    
        def to_yaml_dict(self):
            return {'name': self.name, 'url': self.url}
    
        # @classmethod
        # def from_yaml_dict(cls, dct, yaml_tag):
        #     return CameraBrand(**dct)
    
    
    brand_object = CameraBrand('foo', 'http://foo.com')
    print(yaml.safe_dump(brand_object))
    
    产出。
    !yamlable/myyaml.CameraBrand {name: foo, url: 'http://foo.com'}
    

    print(yaml.safe_load("""!yamlable/myyaml.CameraBrand
    name: foo
    url: http://bar.com
    """))
    

    正确加载对象并显示其字符串表示:

    foo: http://bar.com
    

    请注意,如果您希望在加载对象时执行某些自定义操作,还可以取消注释from_yaml_dict方法。有关详细信息,请参见yamlable文档


  • 这个方法也适用于嵌套定义吗?例如:dict[in][out] = value - alper
    1
    @alper 应该可以,只要 pyyaml 支持它。 - smarie

    3
    如果你仍然想要使用 safe_dump,既不需要使用 dump,也不需要为每个对象编写一个表现器,也没有其他依赖项怎么办呢?
    那么,你可以编写一个方法来捕获所有内容,就像JSON dumpers 中的 "default" 参数,并覆盖 YAML 的 SafeRepresenter 中引发异常的部分。
    这是使用 PyAML 3.12 的概念证明。
    import yaml
    
    yaml.SafeDumper.yaml_representers[None] = lambda self, data: \
        yaml.representer.SafeRepresenter.represent_str(
            self,
            str(data),
        )
    

    例子:

    >>> import collections
    >>> yaml.safe_dump([collections.namedtuple('Foo', 'bar')(1)])                                                                                                  
    '[Foo(bar=1)]\n'
    
    >>> yaml.safe_dump(CameraBrand('Canon', '/cameras/canon/'))
    "'Canon: /cameras/canon/'\n"
    

    注意:关于namedtuple的详细主题,请参见:通过PyYAML序列化命名元组


    1

    您需要为该类实现__repr__方法。您可以直接使用__str__作为__repr__。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接